<div class="chosen-container chosen-container-multi" id="categories_chosen" style="width: 0px;"> 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
<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.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.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%'});
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%');
}
});
});
});
.chosen-container with width: 0px means the .each() body never runs).administrator/templates/vmadmin/html/com_virtuemart/product/product_edit_information.php via vmJsApi::addJScript().// 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>// 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>';// 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-- 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);Page created in 0.515 seconds with 13 queries.