News:

Support the VirtueMart project and become a member

Main Menu

Recent posts

#21
[BUG] Product edit - Categories field collapses to width: 0px after tab switch (Chosen.js + UIkit hidden tab race condition)

VirtueMart version: 4.6.8
Joomla version: 5.x
PHP version: 8.x
Template: vmadmin (default backend template)

Description

When editing a product, switching away from the "Product Information" tab and then switching back causes the Categories multi-select field to collapse to zero width and become unusable. The browser inspector shows:

<div class="chosen-container chosen-container-multi" id="categories_chosen" style="width: 0px;">
Steps to reproduce

  • Open any product for editing (the "Product Information" tab is active by default).
  • Click any other tab, e.g. "Product Images".
  • Click back on "Product Information".
  • The Categories field is now crushed to 0px width and invisible.

The bug also reproduces reliably on first load when the product was previously saved while on a tab other than "Product Information" (UIkit stores the active tab in a cookie - on reload, the "Information" tab starts hidden).

Root cause

Virtuemart.loadCategoryTree() (in
components/com_virtuemart/assets/js/ajax_catree.js) fires an AJAX request during
document.ready. On AJAX success, it calls:

jQuery('select#' + id).chosen({ select_some_options_text: Virtuemart.selectSomeCategory });
// no width option passed

Chosen.js computes its container width from the
<select> element at the moment of initialization. If the AJAX response arrives while the "Product Information"
<li> is hidden by UIkit's switcher (
display: none), the measured width is 0. Chosen then stamps
style="width: 0px;" on its container, and nothing corrects it when the tab becomes visible again.

This is a classic AJAX-vs-hidden-element race condition. The AJAX delay is unpredictable, and once the user has switched tabs, the hidden tab has zero dimensions.

A secondary trigger:
Virtuemart.updateChosenDropdownLayout() (generated by
vmjsapi.php) passes
width: '100%' to
.chosen(), which avoids the problem. But the categories field carries the class
vm-chzn-add, which intentionally excludes it from that initializer (
select:not(.vm-chzn-add)). So the categories select is always initialized through the AJAX path, without a width fallback.

Proposed fix - two-part

1. Root fix (in ajax_catree.js): pass
width: '100%' to the
.chosen() call so the container always gets a percentage-based width instead of a pixel-measured one.

// Before (line ~87):
jQuery('select#'+id).chosen({select_some_options_text: Virtuemart.selectSomeCategory});

// After:
jQuery('select#'+id).chosen({select_some_options_text: Virtuemart.selectSomeCategory, width: '100%'});

2. Safety net (UIkit show event): UIkit 3 dispatches
show with
bubbles: true on each
<li> of the switcher when it becomes active (confirmed in uikit.js -
createEvent(e, bubbles = true)). Adding a listener on the tabs container catches any other Chosen widget that may have suffered the same fate:

jQuery(document).ready(function($) {
var tabsContainer = document.getElementById('vmuikit-admin-ui-tabs');
if (!tabsContainer) { return; }
tabsContainer.addEventListener('show', function(e) {
$(e.target).find('.chosen-container').each(function() {
if (this.style.width === '0px') {
$(this).css('width', '100%');
}
});
});
});

This listener is harmless on every other tab switch (no
.chosen-container with
width: 0px means the
.each() body never runs).

Workaround for existing installations

Add the safety-net JS snippet above to a template override of
administrator/templates/vmadmin/html/com_virtuemart/product/product_edit_information.php via
vmJsApi::addJScript().

Thanks for reading all of that
#22
[BUG] Child products cannot have individual discount rules - product_discount_id ignored on save + missing UI field

Affected versions: 4.6.6, 4.6.8, 4.9.3 (confirmed: present in all three, never fixed)
Severity: High - can cause heavily negative calculated prices displayed as "Price on request" on frontend



Description

When a product is assigned as a child of a parent product, three related bugs prevent the discount rule (`product_discount_id`) from being managed independently for that child.



Bug 1 - Missing UI field in the traditional child list (`views/product/tmpl/product_edit_childs.php`)

The child product list displayed in the parent's "Child product" tab has no column or selector for `product_discount_id`.

Bug 2 - Missing UI field in the type C customfield child list (`models/customfields.php`)

When a product uses a type C customfield ("Multi Variant" / child combo selector), VirtueMart hides the traditional "Child product" tab and replaces it with the type C customfield's own child list panel. This panel also has no column or selector for `product_discount_id`.

This is the more critical case: the type C customfield child list is the only child management interface available when type C is active. Saving the parent through this interface submits child prices WITHOUT `product_discount_id`, which leads directly to Bug 3.

Bug 3 - Model resets discount rule to 0 when not submitted (`models/product.php`)

In `models/product.php`, the `product_discount_id` line was originally wrapped inside an `if (!$isChild)` block, meaning it was never saved for children saved through the parent form. Even after moving it outside that block, if the form does not submit `product_discount_id` (Bug 1 or Bug 2), the fallback `!empty(...) ? ... : 0` sets it to 0.

VirtueMart interprets `product_discount_id = 0` as "apply all active calculation rules", cascading every active discount rule simultaneously. With many active rules this produces a heavily negative `salesPrice` displayed on the frontend as "Price on request".



Consequence

Child products end up with `product_discount_id = NULL` or `0` in `#__virtuemart_product_prices`.

With many active discount rules this produces a heavily negative `salesPrice` (observed: -67 000), displayed on the frontend as "Price on request".

Note on `product_discount_id` values:
  • `NULL` or `0` : all active calculation rules applied (dangerous)
  • `-1` : no discount rule at all (safe default for children with no specific rule)
  • `> 0` : specific discount rule ID (intentional)



Steps to reproduce

  • Create a parent product with a type C customfield ("Multi Variant") and at least one child
  • Edit the parent - the type C panel shows the child list but has no discount column
  • Edit the child product directly, assign a discount rule, save
  • Re-open the parent, re-save from the type C panel: the child's `product_discount_id` is reset to 0
  • Check `#__virtuemart_product_prices` for the child: `product_discount_id = 0`
  • Frontend: all active discount rules cascade onto the child, price goes negative

Reproduces also without type C customfield via the traditional "Child product" tab (Bug 1 + Bug 3).



Proposed fix

Fix 1 - `administrator/components/com_virtuemart/views/product/tmpl/product_edit_childs.php`

Add a `<th>` header and `<td>` cell with `$this->renderDiscountList()` in the traditional child list table:

Code (php) Select
// In <thead> - after the price column header:
<th style="text-align: left !important;"><?php echo vmText::_('COM_VIRTUEMART_PRODUCT_DISCOUNT')?></th>

// In each <tr> child row - after the price <td>:
<td><?php echo $this->renderDiscountList(
    isset(
$child->allPrices[$child->selectedPrice]['product_discount_id'])
        ? (int)
$child->allPrices[$child->selectedPrice]['product_discount_id']
        : -
1,
    
'childs['.$child->virtuemart_product_id.'][mprices][product_discount_id][]'
?>
</td>

Fix 2 - `administrator/components/com_virtuemart/models/customfields.php`

In `renderProductChildLine()`, add a `<th>` in the thead and a `<td>` with the discount selector in each child row:

Code (php) Select
// In thead - after COM_VIRTUEMART_PRODUCT_FORM_PRICE_COST:
$html .= '<th style="text-align: left !important;width:80px;">'.vmText::_('COM_VIRTUEMART_PRODUCT_DISCOUNT').'</th>';

// In each child row - after the product_price / virtuemart_product_price_id inputs:
$selectedDiscount = isset($child->allPrices[$child->selectedPrice]['product_discount_id'])
    ? (int)$child->allPrices[$child->selectedPrice]['product_discount_id']
    : -1;
$discountRates = array();
$discountRates[] = JHtml::_('select.option', '-1', vmText::_('COM_VIRTUEMART_PRODUCT_DISCOUNT_NONE'), 'product_discount_id');
$discountRates[] = JHtml::_('select.option', '0', vmText::_('COM_VIRTUEMART_PRODUCT_DISCOUNT_NO_SPECIAL'), 'product_discount_id');
if (!class_exists('VirtueMartModelCalc')) { VmModel::getModel('calc'); }
foreach (VirtueMartModelCalc::getDiscounts() as $disc) {
    $discountRates[] = JHtml::_('select.option', $disc->virtuemart_calc_id, $disc->calc_name, 'product_discount_id');
}
$html .= '<td>'.JHtml::_('select.genericlist', $discountRates,
    'childs['.$child->virtuemart_product_id.'][mprices][product_discount_id][]',
    'class="vm-chzn-add"', 'product_discount_id', 'text', $selectedDiscount, '[').'</td>';

Fix 3 - `administrator/components/com_virtuemart/models/product.php`

Move `product_discount_id` outside the `if (!$isChild)` block, AND change the fallback from hard-coding 0 to only writing the value when explicitly submitted:

Code (php) Select
// BEFORE (buggy - inside if (!$isChild), never saved for children):
if (!$isChild){
    // ...
    $pricesToStore['product_discount_id'] = !empty($data['mprices']['product_discount_id'][$k])
        ? (int)$data['mprices']['product_discount_id'][$k] : 0; // resets to 0 if missing
    // ...
}

// AFTER (outside the block, preserved when not submitted):
if (!$isChild){
    // ... other fields only ...
}
if (isset($data['mprices']['product_discount_id'][$k])) {
    $pricesToStore['product_discount_id'] = (int)$data['mprices']['product_discount_id'][$k];
}
// If not in POST, existing DB value is preserved



Immediate workaround (SQL)

Set `product_discount_id = -1` (no discount rule) for affected child products:

Code (sql) Select
-- Diagnostic
SELECT p.virtuemart_product_id, p.product_parent_id,
       pp.product_price, pp.product_discount_id
FROM #__virtuemart_products p
LEFT JOIN #__virtuemart_product_prices pp USING (virtuemart_product_id)
WHERE p.product_parent_id > 0;

-- Fix: NULL/0 -> -1 for children with no intentional discount
UPDATE #__virtuemart_product_prices
SET product_discount_id = -1
WHERE virtuemart_product_id IN (
    SELECT virtuemart_product_id
    FROM #__virtuemart_products
    WHERE product_parent_id > 0
)
AND (product_discount_id IS NULL OR product_discount_id = 0);
#23
Virtuemart Development and bug reports / recaptcha wasnt rendering
Last post by PRO - June 24, 2026, 20:19:56 PM
Captcha field was failing to render , was wrapped in editor html, the onInit, and onDisplay was causing this.

shopfunctionsF


comment these out
            //   $app = JFactory::getApplication();
            //   $results = $app->triggerEvent('onInit', [$id]);
            //   $output  = $app->triggerEvent('onDisplay', [$reCaptchaName, $id, 'g-recaptcha required']);



and added
   $captcha = \Joomla\CMS\Captcha\Captcha::getInstance($reCaptchaName);
    $output = [$captcha->display($id, $id, 'g-recaptcha required')];



so fixed as so

   } else {
            //   $app = JFactory::getApplication();
            //   $results = $app->triggerEvent('onInit', [$id]);
            //   $output  = $app->triggerEvent('onDisplay', [$reCaptchaName, $id, 'g-recaptcha required']);
            $captcha = \Joomla\CMS\Captcha\Captcha::getInstance($reCaptchaName);
    $output = [$captcha->display($id, $id, 'g-recaptcha required')];
            }


#24
3rd party extension / USPS Shipping for VirtueMart 4...
Last post by Jumbo! - June 24, 2026, 00:57:01 AM
Real-time USPS shipping rates in VirtueMart, on the new USPS REST API

The old USPS Web Tools XML API was retired in January 2026. That change broke a lot of older USPS shipping plugins, and many stores suddenly stopped showing live postage at checkout.

VP USPS Shipping is built on the new USPS REST API, the official replacement that uses OAuth 2.0 and JSON. Your store keeps quoting accurate USPS rates, fetched live at checkout from the real cart weight, the package dimensions and the customer's destination.

You can use retail or commercial base rates, and commercial rates do not need a USPS account number. Built-in rate caching keeps you within the USPS limit of 60 requests per hour, and you can point the plugin at the production or the staging environment while you test.

Domestic services
  • Priority Mail and Priority Mail Express
  • USPS Ground Advantage
  • First-Class Package Service
  • Media Mail and Library Mail
  • Small, Medium and Large Flat Rate Boxes
  • Flat Rate, Legal and Padded Envelopes, for Priority Mail and Priority Mail Express

International services
  • Priority Mail International and Priority Mail Express International
  • First-Class Package International Service
  • Global Express Guaranteed
  • International flat rate boxes and envelopes

A FEW THINGS THAT SET IT APART
  • Smart Flat Rate. A 3D box-packing algorithm checks that the products physically fit inside a flat rate container before that container is offered, so you never quote a box the order cannot go in.
  • Multi-box shipping for heavy orders, with configurable outer box dimensions per method, a cart weight padding option and a machinable toggle.
  • Show Cheapest Option Only. Collapse the results to the single lowest-priced method that qualifies when you want a simpler checkout.
  • Handling fees as a flat amount, a percentage of the shipping cost, or a percentage of the cart total. Per-method tax rules and logos are supported too.
  • Restrictions by destination country, ZIP or postal range, order amount, product category and a blocking category. On VirtueMart 4.6 and later it uses the VirtueMart core restrictions, which also add blocking countries, and it falls back to its own restrictions on older versions.

One thing to be clear about. Rates come back in US Dollars with no currency conversion, and the plugin is built for stores that ship from a US address, since USPS only collects packages inside the US.

No hacks, no manual file edits. Install the plugin with the normal Joomla Extensions Installer, enter your USPS Client ID and Secret, enable it, and you are ready to go.



System Requirement: VirtueMart 3.8+, 4.0, 4.2, 4.4 or 4.6 | Joomla 3, 4 or 5 | PHP 7.1+ (PHP 8.1+ recommended for Joomla 5) | cURL extension enabled

Product Details: https://www.virtueplanet.com/extensions/usps-shipping
Documentation: User Guide
Support: https://www.virtueplanet.com/forum/vp-usps-shipping-plugin
Pre-sale Queries: https://www.virtueplanet.com/contact-us

VirtuePlanet - https://www.virtueplanet.com

#25
Unfortunately, there has been no response from the Virtuemart developers so far. There's no news on this matter. If anyone knows of an update, please let me know. This is very important for Virtuemart stores users.
#26
I repeat question, will there be an update? It's very important.
#27
General Questions / Re: Issues with add to Cart . ...
Last post by hotrod - June 21, 2026, 12:21:02 PM
I really Can't at this time..  All my sites were built With Rocket theme.. And gantry 5..  They are working on a plugin to make the RT templates compatible w J5..  For now.. the J4 sites are ok..  at some point I need to change servers to be able to run J5..   Getting the J4 site up is just temporary... It's 99.9% there...   Prob need to update it..  I have a ticket in to buy the update..  We'll see..
#28
General Questions / Re: Issues with add to Cart . ...
Last post by iWim - June 19, 2026, 15:41:32 PM
J4 is old too.

I recomend to install your webshop locally on WAMPserver or MAMP.
Use Akeeba Backup + Kickstart.

On this testsite, update it to J5 and VM 4.6.8. See if that fixes the issue. (VM4.6.8 uses jquery-ui.min.js?vmver=1.13.2)

When you update on a testsite first you can test changes and you'll get familiar with the update process.
#29
As of today, this is the law.

It is mandatory for webshops selling in the EU to clearly display an option for customers to undo a sale.

Shop owners risk fines if they don't comply.

VM needs to implement this yesterday.
#30
General Questions / Re: Issues with add to Cart . ...
Last post by hotrod - June 19, 2026, 15:15:42 PM
This has been sporadic.. I am moving away from this and going back to the updated J4 site... I never finished..