News:

Support the VirtueMart project and become a member

Main Menu

Recent posts

#11
General Questions / Re: Issues with add to Cart . ...
Last post by PRO - June 30, 2026, 16:37:17 PM
have you tested the default theme?

nobody can look without a url
#12
General Questions / Re: Google Merchant Center Pro...
Last post by PRO - June 30, 2026, 16:35:45 PM
Have you ever tried using schema ?
#13
Thank You... Found this issue also drove me nuts...  ChatGPT made it very simple..  Here is the detailed info...

/public_html/components/com_virtuemart/sublayouts/snippets.php

Replace:

if ($finalstk != 0) {
    $stockog = 'InStock';
}
if ($finalstk == 0) {
    $stockog = 'OutOfStock';
}

with:

$stockog = 'InStock';

That's all you need.
#14
It's not malware but an old insecure version of the script. VM no longer ships the file but it is also not deleted on update. You can delete it if you don't have any old templates and extensions that might be using it.
#15
My server has found malware in this VM file on all of my websites:

components/com_virtuemart/assets/js/jquery.min.js

Just for your info.
#16
Virtuemart Development and bug reports / Re: recaptcha wasnt rendering
Last post by PRO - June 29, 2026, 18:44:34 PM
neew joomla does not come with native captcha

Most download a 3rd party one,
like so
https://github.com/SharkyKZ/joomla-recaptcha-v3-plugin
#17
General Questions / Google Merchant Center Product...
Last post by hotrod - June 29, 2026, 14:08:08 PM
Since moving my store to J4 and VM 4.2.4  I lost my Data Feed for Google Merchant..  I don't even remember what I had or where I got it.    Most of the answers here on the question show dead links..

Any help would be great..

Rod
#18
Administration & Configuration / Re: blank pdf delivery note an...
Last post by panne003 - June 26, 2026, 16:25:06 PM
### Opgelost: Lege PDF-facturen en afleverbonnen bij PAY.nl Billink (VirtueMart 4 / Joomla 5)

Na bijna twee jaar zoeken heb ik eindelijk de oorzaak gevonden.

**Omgeving**

* Joomla 5.4.6
* VirtueMart 4.6.8
* PAY.nl plugin 4.0.3
* Billink Achteraf Betalen

**Probleem**

Alleen bij Billink werden de factuur-PDF en afleverbon volledig leeg gegenereerd.

De HTML van de factuur was volledig correct, maar de PDF bevatte alleen een lege pagina. Andere betaalmethoden zoals iDEAL werkten probleemloos.

Na uitgebreid debuggen van:

* VirtueMart
* TCPDF
* PAY.nl plugin
* vmpdf.php

bleek uiteindelijk dat de oorzaak helemaal niet in de plugin zat.

### Oorzaak

De naam van de betaalmethode bevatte:

```
Billink Achteraf betalen <€100,-
```

Het `<`-teken werd door TCPDF als het begin van een HTML-tag geïnterpreteerd.

Daardoor kon TCPDF de HTML niet meer correct verwerken en werd een lege PDF aangemaakt.

### Oplossing

Wijzig de naam van de betaalmethode bijvoorbeeld naar:

```
Billink Achteraf betalen tot €100,-
```

of

```
Billink Achteraf betalen (€0 - €100)
```

Na deze wijziging werken direct weer:

* PDF-facturen
* Afleverbonnen
* E-mails met PDF-bijlage

Misschien helpt dit iemand anders die hetzelfde probleem heeft.

### Solved: Empty PDF invoices and packing slips with PAY.nl Billink (VirtueMart 4 / Joomla 5)

After almost two years of troubleshooting I finally found the root cause.

**Environment**

* Joomla 5.4.6
* VirtueMart 4.6.8
* PAY.nl plugin 4.0.3
* Billink Post-Payment

**Problem**

Only Billink generated completely empty invoice PDFs and packing slips.

The invoice HTML was generated correctly, but the PDF itself contained no content.

Other payment methods (such as iDEAL) worked perfectly.

After debugging:

* VirtueMart
* TCPDF
* PAY.nl plugin
* vmpdf.php

it turned out that neither VirtueMart nor the PAY.nl plugin was responsible.

### Root cause

The payment method name contained:

```
Billink Achteraf betalen <€100,-
```

The `<` character was interpreted by TCPDF as the start of an HTML tag.

As a result, TCPDF failed to render the HTML correctly and generated an almost empty PDF.

### Solution

Rename the payment method, for example:

```
Billink Post-Payment up to €100
```

or

```
Billink Post-Payment (€0 - €100)
```

After removing the `<` character:

* Invoice PDFs are generated correctly.
* Packing slips work again.
* PDF attachments in customer emails are generated correctly.

Hopefully this saves someone else many hours of debugging.


#19
And which Captcha solution will work with this code change?
#20
Feature Request - Child product ordering via drag-and-drop in type C (Multi Variant) customfield panel

VirtueMart version: 4.6.8 (also checked on 4.6.6 and 4.9.3)
Joomla version: 5.4.6
PHP: 8.1 - 8.5

Summary

When a product uses a type C "Multi Variant" customfield, the child product table in the backend panel provides no way to reorder variants. The display order is determined by sortChildIds() which sorts by option combinations - not by the merchant's preferred order. Reordering is a common need (e.g. keeping size variants sorted smallest to largest, or putting the most popular variant first).

Current behaviour

The child table in the type C panel renders with a vmicon-16-move handle in each row and jQuery UI Sortable is already initialised on #syncro (the tbody). Dragging rows works visually but nothing is ever saved - the update callback is absent and no pordering value is submitted with the form.

After a save, sortChildIds() re-sorts children by their option combination order, overriding whatever was in the database.

A second issue: the parent product (which is itself a variant in type C products) is always forced to the first row via array_unshift, regardless of drag order. Its pordering is also never saved because the childs save loop in product.php skips entries where $productId == $data['virtuemart_product_id'].

Expected behaviour

Dragging any row (including the parent variant) should update and persist the display order. The frontend already uses ORDER BY pordering ASC via getAllProductChildIds() - the infrastructure is in place. Only the admin side is missing.

Fix - three changes

1. models/customfields.php - add hidden pordering input in renderProductChildLine() (first <td>, before the link):

$html .= '<td style="white-space:nowrap;">'
. '<span class="vmicon vmicon-16-move"
      style="cursor:move;font-size:20px;color:#666;vertical-align:middle;margin:0 6px 0 5px;"
      title="Reorder">⋮⋮</span>'
. JHTML::_('link', ...)
. '<input type="hidden" class="vmchild-ordering"
      name="childs[' . $child->virtuemart_product_id . '][pordering]"
      value="' . (int)($child->pordering ?? 0) . '" />'
. '</td>';

2. models/customfields.php - replace sortChildIds + array_unshift block with pordering-aware sort:

if (isset($childIds[$product_id])) {
$sorted = self::sortChildIds($product_id, $childIds[$product_id], $field->options);
$allIds = array_merge([$product_id], array_values($childIds[$product_id]));
$db = JFactory::getDBO();
$db->setQuery('SELECT virtuemart_product_id, pordering FROM #__virtuemart_products'
. ' WHERE virtuemart_product_id IN (' . implode(',', array_map('intval', $allIds)) . ')');
$porderingMap = $db->loadAssocList('virtuemart_product_id', 'pordering');
$sorted[] = ['parent_id' => $product_id, 'vm_product_id' => $product_id]; // parent included
usort($sorted, function ($a, $b) use ($porderingMap) {
$posA = isset($porderingMap[$a['vm_product_id']]) ? (int)$porderingMap[$a['vm_product_id']] : 999;
$posB = isset($porderingMap[$b['vm_product_id']]) ? (int)$porderingMap[$b['vm_product_id']] : 999;
return $posA - $posB;
});
} else {
$sorted[] = ['parent_id' => $product_id, 'vm_product_id' => $product_id];
}
// array_unshift(...) removed - parent is now part of the sorted array

3. models/customfields.php - add update callback to the #syncro sortable (existing JS block):

jQuery(document).ready(function ($) {
$('#syncro').sortable({
cursorAt: { top: 0, left: 0 },
handle: '.vmicon-16-move',
update: function (event, ui) {
$(this).find('.vmchild-ordering').each(function (index, el) {
$(el).val(index);
});
}
});
$('#syncro .vmchild-ordering').each(function (index, el) {
$(el).val(index);
});
});

4. models/product.php - save parent's pordering when submitted from the type C panel:

In the foreach ($data['childs'] as $productId => $child) loop, add an else branch for the case where $productId == $data['virtuemart_product_id']:

} else {
// Parent variant: only update pordering if explicitly submitted
if (isset($child['pordering'])) {
$db = JFactory::getDBO();
$db->setQuery('UPDATE #__virtuemart_products SET pordering = '
. (int)$child['pordering']
. ' WHERE virtuemart_product_id = ' . (int)$productId);
$db->execute();
}
}

Why it matters

Merchants managing products with several size or format variants (e.g. 100 cm / 130 cm / 150 cm / 200 cm / 250 cm) have no control over which variant appears first in the selector on the product page. Making the existing drag infrastructure actually save the order would be a low-risk, high-value improvement. The pordering field and the ORDER BY pordering clause are already in place on the frontend side - the admin panel simply never writes to them.

Thanks again for reading this...