News:

Looking for documentation? Take a look on our wiki

Main Menu

Recent posts

#1
Because the prices are unnecessary complex, old vm1 stuff. Since vm2 it works a different way.

We have rules which works per categories, so using the "old parameters" just create more work than necessary.
#2
I think that is fixed with version 4.2.12
#3
hmmm, maybe too strong filter. What happens if you store a default without html? just something like "test", then we may get a grib of this problem.
#4
If you store a parent, which has the extra tab for children, than it takes this values for the children.

that works most time correctly, but if someone buys something meanwhile, it wont work 100%.

But that is a common problem for our stock feature.
#6
I have a very strange and weird bug. The stock gets automatically reverted back to it's old value. My client tells me it isn't that they do it by themselves, it gets done by the system. Any idea how this happens?

I checked all the plugins and components I have and none of them have anything to do with the stock. I'm very confused why this happens.

I think the best solution here is to add logging to the stock changes. I added in 2 places logging. Maybe a checkbox needs to be added so the logging only gets done when the user actually wants it, but for now I just hardcode it.

@Milbo

      public function updateStockInDB ($product, $amount, $signInStock, $signOrderedStock) {
           

        // Get the currently logged-in user
        $user = Factory::getUser();
        $userId = $user->id;
        $username = $user->username;
   
        // Log the start of the stock update process
        JLog::addLogger(
            array(
                'text_file' => 'com_virtuemart.log.php', // Name of the log file
                'text_entry_format' => '{DATETIME} {PRIORITY} {CATEGORY} {MESSAGE}' // Log entry format
            ),
            JLog::ALL, // Log all levels of messages
            array('com_virtuemart') // Categories to log
        );
   
        JLog::add(
            sprintf(
                'User %s (ID: %d) is attempting to update stock for product ID %d with amount %f, signInStock %s, signOrderedStock %s',
                $username, $userId, $product->virtuemart_product_id, $amount, $signInStock, $signOrderedStock
            ),
            Log::INFO,
            'com_virtuemart'
        );

        vmdebug('updateStockInDB start ', $signInStock, $signOrderedStock);
        $validFields = array('=', '+', '-');
        if (!in_array ($signInStock, $validFields)) {
            return FALSE;
        }
        if (!in_array ($signOrderedStock, $validFields)) {
            return FALSE;
        }

        $lproduct = $this->getProductSingle($product->virtuemart_product_id);
        if($lproduct->shared_stock){
            $productId = $lproduct->product_parent_id;
        } else {
            $productId = $product->virtuemart_product_id;
        }

        $amount = (float)$amount;
        $update = array();

        if ($signInStock != '=' or $signOrderedStock != '=') {

            if ($signInStock != '=') {
                $update[] = '`product_in_stock` = `product_in_stock` ' . $signInStock . $amount;

                if (strpos ($signInStock, '+') !== FALSE) {
                    $signInStock = '-';
                }
                else {
                    $signInStock = '+';
                }
                $update[] = '`product_sales` = `product_sales` ' . $signInStock . $amount;

            }
            if ($signOrderedStock != '=') {
                $update[] = '`product_ordered` = `product_ordered` ' . $signOrderedStock . $amount;
            }
            $q = 'UPDATE `#__virtuemart_products` SET ' . implode (", ", $update) . ' WHERE `virtuemart_product_id` = ' . (int)$productId;

            $db = JFactory::getDbo();
            $db->setQuery ($q);
            $db->execute ();
            //vmdebug('updateStockInDB executed query ', $q);
            //The low on stock notification comes now, when the people ordered.
            //You need to know that the stock is going low before you actually sent the wares, because then you ususally know it already yourself
            //note by Max Milbers
            if ($signInStock == '+' or $signOrderedStock == '+') {

                $q = 'SELECT (IFNULL(`product_in_stock`,"0")-IFNULL(`product_ordered`,"0")) < IFNULL(`low_stock_notification`,"0") '
                . 'FROM `#__virtuemart_products` '
                . 'WHERE `virtuemart_product_id` = ' . (int)$productId;
                $db->setQuery ( $q );
                //vmdebug('Check for low stock ',$q);
                if ($db->loadResult () == 1) {
                    vmdebug('Check for low stock said therre is a low stock ');
                    $this->lowStockWarningEmail( $productId) ;
                }
            }
        }

    }


And then I also added the logging to the store when a user changes the stock manually:

public function store (&$data) {

        vRequest::vmCheckToken();

        if(!vmAccess::manager('product.edit')){
            vmError('You are not a vendor or administrator, storing of product cancelled');
            return FALSE;
        }

        if ($data and is_object($data)) {
            $data = get_object_vars($data);
        }

        $isChild = FALSE;
        if(!empty($data['isChild'])) $isChild = $data['isChild'];

        if (isset($data['intnotes'])) {
            $data['intnotes'] = trim ($data['intnotes']);
        }

        // Setup some place holders
        $product_data = $this->getTable ('products');

        $data['new'] = '1';
        if(!empty($data['virtuemart_product_id'])){
            $product_data -> load($data['virtuemart_product_id']);
            $data['new'] = '0';
        }
        if( (empty($data['virtuemart_product_id']) or empty($product_data->virtuemart_product_id)) and !vmAccess::manager('product.create')){
            vmWarn('Insufficient permission to create product');
            return false;
        }

        $vendorId = vmAccess::isSuperVendor();
        $vM = VmModel::getModel('vendor');
        $ven = $vM->getVendor($vendorId);

        if(VmConfig::get('multix','none')!='none' and !vmAccess::manager('core')){

            if($ven->max_products!=-1){
                $this->setGetCount (true);
                //$this->setDebugSql(true);
                parent::exeSortSearchListQuery(2,'virtuemart_product_id',' FROM #__virtuemart_products',' WHERE ( `virtuemart_vendor_id` = "'.$vendorId.'" AND `published`="1") ');
                $this->setGetCount (false);
                if($ven->max_products<($this->_total+1)){
                    vmWarn('You are not allowed to create more than '.$ven->max_products.' products');
                    return false;
                }
            }
        }

        if(!vmAccess::manager('product.edit.state')){
            if( (empty($data['virtuemart_product_id']) or empty($product_data->virtuemart_product_id))){
                $data['published'] = 0;
            } else {
                $data['published'] = $product_data->published;
            }
        }

        //Set the decimals like product packaging
        foreach(self::$decimals as $decimal){
            if (array_key_exists ($decimal, $data)) {
                if(!empty($data[$decimal])){
                    $data[$decimal] = str_replace(',','.',$data[$decimal]);
                    //vmdebug('Store product '.$data['virtuemart_product_id'].', set $decimal '.$decimal.' = '.$data[$decimal]);
                } else {
                    $data[$decimal] = null;
                    $product_data->{$decimal} = null;
                    //vmdebug('Store product '.$data['virtuemart_product_id'].', set $decimal '.$decimal.' = null');
                }
            }
        }

        if($ven->force_product_pattern>0 and empty($data['product_parent_id']) and $ven->force_product_pattern!=$data['virtuemart_product_id']){
            $data['product_parent_id'] = $ven->force_product_pattern;
        }

        //We prevent with this line, that someone is storing a product as its own parent
        if(!empty($data['product_parent_id']) and !empty($data['virtuemart_product_id']) and $data['product_parent_id'] == $data['virtuemart_product_id']){
            $data['product_parent_id'] = 0;
        }

        $product_data->has_prices = (isset($data['mprices']['product_price']) and count($data['mprices']['product_price']) > 0)? 1:0;

        if (!$isChild) {
            $product_data->has_shoppergroups = empty($data['virtuemart_shoppergroup_id'])? 0:1;
            $product_data->has_manufacturers = empty($data['virtuemart_manufacturer_id'])? 0:1;
            //$product_data->has_medias = !empty($data['virtuemart_media_id']) or !empty($data['media']['virtuemart_media_id'])? 1:0;
            $product_data->has_categories = empty($data['categories'])? 0:1;
            if(!empty($data['virtuemart_media_id']) or !empty($data['media']['virtuemart_media_id']) or !empty($data['media']['media_action'])){
                $product_data->has_medias = 1;
            } else {
                $product_data->has_medias = 0;
            }
        }


        vDispatcher::importVMPlugins('vmcustom');
        vDispatcher::trigger('plgVmBeforeStoreProduct',array(&$data, &$product_data));

        $stored = $product_data->bindChecknStore($data, false);

       

        if(!$stored ){
            vmError('You are not an administrator or the correct vendor, storing of product cancelled');
            vmdebug('You are not an administrator or the correct vendor, storing of product cancelled', $data, $product_data->loadFieldValues());
            return FALSE;
        }

        $this->_id = $data['virtuemart_product_id'] = (int)$product_data->virtuemart_product_id;

        if (empty($this->_id)) {
            vmError('Product not stored, no id');
            return FALSE;
        }

        //We may need to change this, the reason it is not in the other list of commands for parents
        if (!$isChild) {
            $modelCustomfields = VmModel::getModel ('Customfields');
            $modelCustomfields->storeProductCustomfields ('product', $data, $product_data->virtuemart_product_id);
        }

        // Get old IDS
        $old_price_ids = $this->loadProductPrices($this->_id,array(0),false);

        if (isset($data['mprices']['product_price']) and count($data['mprices']['product_price']) > 0){

            foreach($data['mprices']['product_price'] as $k => $product_price){

                $pricesToStore = array();
                $pricesToStore['virtuemart_product_id'] = $this->_id;
                $pricesToStore['virtuemart_product_price_id'] = (int)$data['mprices']['virtuemart_product_price_id'][$k];

                if (!$isChild){
                    //$pricesToStore['basePrice'] = $data['mprices']['basePrice'][$k];
                    $pricesToStore['product_override_price'] = $data['mprices']['product_override_price'][$k];
                    $pricesToStore['override'] = isset($data['mprices']['override'][$k])?(int)$data['mprices']['override'][$k]:0;
                    $pricesToStore['virtuemart_shoppergroup_id'] = (int)$data['mprices']['virtuemart_shoppergroup_id'][$k];
                    $pricesToStore['product_tax_id'] = !empty($data['mprices']['product_tax_id'][$k])? (int)$data['mprices']['product_tax_id'][$k]:0;
                    $pricesToStore['product_discount_id'] = !empty($data['mprices']['product_discount_id'][$k])? (int)$data['mprices']['product_discount_id'][$k]:0;
                    $pricesToStore['product_currency'] = !empty($data['mprices']['product_currency'][$k])? (int)$data['mprices']['product_currency'][$k] : $ven->vendor_currency;
                    $pricesToStore['product_price_publish_up'] = !empty($data['mprices']['product_price_publish_up'][$k])? $data['mprices']['product_price_publish_up'][$k]:0;
                    $pricesToStore['product_price_publish_down'] = !empty($data['mprices']['product_price_publish_down'][$k])? $data['mprices']['product_price_publish_down'][$k]:0;
                    $pricesToStore['price_quantity_start'] = !empty($data['mprices']['price_quantity_start'][$k])? (int)$data['mprices']['price_quantity_start'][$k]:0;
                    $pricesToStore['price_quantity_end'] = !empty($data['mprices']['price_quantity_end'][$k])? (int)$data['mprices']['price_quantity_end'][$k]:0;
                }

                if (!$isChild and isset($data['mprices']['use_desired_price'][$k]) and $data['mprices']['use_desired_price'][$k] == "1") {

                    $calculator = calculationHelper::getInstance ();
                    if(isset($data['mprices']['salesPrice'][$k])){
                        $data['mprices']['salesPrice'][$k] = str_replace(array(',',' '),array('.',''),$data['mprices']['salesPrice'][$k]);
                    }
                    $pricesToStore['salesPrice'] = $data['mprices']['salesPrice'][$k];
                    $pricesToStore['product_price'] = $data['mprices']['product_price'][$k] = $calculator->calculateCostprice ($this->_id, $pricesToStore);
                    unset($data['mprices']['use_desired_price'][$k]);
                } else {
                    if(isset($data['mprices']['product_price'][$k]) ){
                        $pricesToStore['product_price'] = $data['mprices']['product_price'][$k];
                    }

                }

                if ($isChild) $childPrices = $this->loadProductPrices($this->_id,array(0),false);

                if ((isset($pricesToStore['product_price']) and $pricesToStore['product_price']!='' and $pricesToStore['product_price']!=='0') || (isset($childPrices) and is_array($childPrices) and count($childPrices)>1)) {

                    if ($isChild) {

                        if(is_array($old_price_ids) and count($old_price_ids)>1){

                            //We do not touch multiple child prices. Because in the parent list, we see no price, the gui is
                            //missing to reflect the information properly.
                            $pricesToStore = false;
                            $old_price_ids = array();
                        } else {
                            unset($data['mprices']['product_override_price'][$k]);
                            unset($pricesToStore['product_override_price']);
                            unset($data['mprices']['override'][$k]);
                            unset($pricesToStore['override']);
                        }

                    }

                    if($pricesToStore){
                        $toUnset = array();
                        if (!empty($old_price_ids) and count($old_price_ids) ) {
                            foreach($old_price_ids as $key => $oldprice){
                                if($pricesToStore['virtuemart_product_price_id'] == $oldprice['virtuemart_product_price_id'] ){
                                    $pricesToStore = array_merge($oldprice,$pricesToStore);
                                    $toUnset[] = $key;
                                }
                            }
                        }
                        $this->updateXrefAndChildTables ($pricesToStore, 'product_prices',$isChild);

                        foreach($toUnset as $key){
                            unset( $old_price_ids[ $key ] );
                        }
                    }
                }
            }
        }

        if (!empty($old_price_ids) and count($old_price_ids) ) {
            $oldPriceIdsSql = array();
            foreach($old_price_ids as $oldPride){
                $oldPriceIdsSql[] = $oldPride['virtuemart_product_price_id'];
            }
            $db = JFactory::getDbo();
            // delete old unused Prices
            $db->setQuery( 'DELETE FROM `#__virtuemart_product_prices` WHERE `virtuemart_product_price_id` in ("'.implode('","', $oldPriceIdsSql ).'") ');
            $err = '';
            try {
                $db->execute();
            } catch(Exception $e) {
                $err = $e->getMessage();
            }
           
            if(!empty($err)){
                vmWarn('In store prodcut, deleting old price error',$err);
            }
        }

        if (!empty($data['childs'])) {
            foreach ($data['childs'] as $productId => $child) {
                if(empty($productId)) continue;
                if($productId!=$data['virtuemart_product_id']){

                    if(empty($child['product_parent_id'])) $child['product_parent_id'] = $data['virtuemart_product_id'];
                    $child['virtuemart_product_id'] = $productId;

                    if(!empty($child['product_parent_id']) and $child['product_parent_id'] == $child['virtuemart_product_id']){
                        $child['product_parent_id'] = 0;
                    }

                    $child['isChild'] = $this->_id;
                    $this->store ($child);
                }
            }
        }

        if (!$isChild) {

            $data = $this->updateXrefAndChildTables ($data, 'product_shoppergroups');

            $data = $this->updateXrefAndChildTables ($data, 'product_manufacturers');

            $storeCats = false;
            if (empty($data['categories']) or (!empty($data['categories'][0]) and $data['categories'][0]!="-2")){
                $storeCats = true;
            }

            if($storeCats){
                if (!empty($data['categories']) && count ($data['categories']) > 0) {
                    if(VmConfig::get('multix','none')!='none' and !vmAccess::manager('managevendors')){

                        if($ven->max_cats_per_product>=0){
                            while($ven->max_cats_per_product<count($data['categories'])){
                                array_pop($data['categories']);
                            }
                        }

                    }
                    $data['virtuemart_category_id'] = $data['categories'];
                } else {
                    $data['virtuemart_category_id'] = array();
                }
                $data = $this->updateXrefAndChildTables ($data, 'product_categories');
            }

            // Update waiting list
            if (!empty($data['notify_users'])) {
                if ($data['product_in_stock'] > 0 && $data['notify_users'] == '1') {
                    $waitinglist = VmModel::getModel ('Waitinglist');
                    $waitinglist->notifyList ($data['virtuemart_product_id']);
                }
            }

            // Process the images
            $mediaModel = VmModel::getModel ('Media');
            $mediaModel->storeMedia ($data, 'product');

        }

        $oldStock = $product_data->product_in_stock;

        // Attempt to store product data
       

        // Get the currently logged-in user
        $user = Factory::getUser();
        $userId = $user->id;
        $username = $user->username;

        // Initialize the logger
        Log::addLogger(
            array(
                'text_file' => 'com_virtuemart.log.php', // Name of the log file
                'text_entry_format' => '{DATETIME} {PRIORITY} {CATEGORY} {MESSAGE}' // Log entry format
            ),
            Log::ALL, // Log all levels of messages
            array('com_virtuemart') // Categories to log
        );

        // Check if the stock has changed and log the changes
        if ($oldStock != $data['product_in_stock']) {
            Log::add(
                sprintf(
                    'User %s (ID: %d) changed stock for product ID %d from %d to %d',
                    $username, $userId, $product_data->virtuemart_product_id, $oldStock, $data['product_in_stock']
                ),
                Log::INFO,
                'com_virtuemart'
            );
        }

        $cache = VmConfig::getCache('com_virtuemart_orderby_manus','callback');
        $cache->clean();

        vDispatcher::trigger('plgVmAfterStoreProduct',array(&$data, &$product_data));

        return $product_data->virtuemart_product_id;
    }
#7
Product pricing / for those who wants to remove ...
Last post by rdcustom - Today at 12:23:58 PM
did a trick today, I post here for future reference in case of updates:

currencydisplay.php

on line 301

add:

$res = preg_replace('/\,00/', '', $res);
$res = rtrim($res, ",");

this will show prices without useless ",00"

125,00 will result in "125"
#8
Product pricing / Re: If there's an override, cu...
Last post by rdcustom - Today at 11:46:22 AM
I found myself the bug:

in calculationh.php - line 416

need to remove:

"if($variant){
            $this->productPrices['discountedPriceWithoutTax'] += $variant;
         }

"
#9
Hi,
Any idea how to fix this?
#10
General Questions / Issues with Coupon Functionali...
Last post by k2mdedia - Yesterday at 21:59:09 PM

I'm experiencing two issues with the coupon functionality in Virtuemart 4.2.4 on Joomla 4:

- When I set a coupon to be used only with a specific product, apply it to the cart, and then remove the item, the cart does not automatically reload. After manually refreshing the page, the product does get removed, but the coupon remains applied.

- Even though the coupon is configured to be valid only for specific products, it incorrectly reappears and gets applied to any new products added to the cart after the original product is removed.