2

I have made a script which copies one customer group to another for products. However after running this script, I found out that it a) clears the URL key of the product and b) sets the visibility to Catalog, Search for all products. Also the script takes a verrrry long time (it reinitialised connection after 40 min, there are only some 300 products). Can anyone help me out? This is the code:

function productCallback($args)
{
$product = Mage::getModel('catalog/product');
$product->setData($args['row']);
$product->getGroupPrice();
$groupPrices = $product->getData('group_price');
$price = 0;
$from = $_POST['from_group'];
$to = $_POST['to_group'];

foreach($groupPrices as $key=>$value)
{
    foreach($value as $subkey => $subvalue)
    {
        if ($subkey == "cust_group" && $subvalue == $from) 
        {
            $price = $value['price'];
            $newGroupPrice = array(array ('website_id'=>0,
            'cust_group'=>$to,
            'price'=>$price,
            'website_price'=>$price,
            'all_groups'=>0));
            echo "price: ".$price . "<br/>";

            $newGroupPrices = array_merge($groupPrices,$newGroupPrice);

            $product->setData('group_price',$newGroupPrices);
            $product->save();
            break;
        }
    }
}
}

$products = Mage::getModel('catalog/product')->getCollection()
                ->addAttributeToSelect('sku')
                ->addAttributeToSelect(array('group_price'));

if($from > -1)  
   Mage::getSingleton('core/resource_iterator')->walk($products->getSelect(), array('productCallback'), array('arg1' => '===='));

2 Answers 2

3
+50

you are calling $product->save() without calling $product->load(). This results in loss of data (or wrong data). You should never call save on an item from a collection. You should call load before doing that.
Also, calling save in a loop results in huge performance issues. save is very resource consuming.
If this is a "one time only script" I recommend you the fast (but a bit risky) method of reading and writing directly from/to the database.
The only table that contains the group prices is catalog_product_entity_group_price.
You can read the values for a product from a customer group on a specific website. If there is a record for that, then update it, otherwise insert a new line.
You can even do this from a single insert statement:

INSERT INTO
    catalog_product_entity_group_price
SET 
    `entity_id` = the product id here,
    `all_groups`  = 1 or 0...depending if applies to all groups or not
    `customer_group_id`  = the customer group here
    `value`  = the actual price in the base currency
    `website_id` the website id here
ON DUPLICATE KEY UPDATE
    `value` = the actual price in base currency

This should solve your problems fast, just make sure you backup your db before starting and you rebuild the indexes when finishing.

6
  • Hi Marius, can you show me an example where/how to call $product->load() in my code?
    – Robin
    Sep 22, 2015 at 8:57
  • not sure how $args looks like in your method, but it should be something like this $product = Mage::getModel('catalog/product'); $product->load($args['row']['id']) or something like that. `
    – Marius
    Sep 22, 2015 at 9:00
  • also I thought that entity_id from your SQL example would equal $product->getId() but they are not the same o0
    – Robin
    Sep 22, 2015 at 9:19
  • ok sorry they are the same. I was looking in the wrong server >_>. I'll try both those solutions.
    – Robin
    Sep 22, 2015 at 9:35
  • unfortunately, using load($args['row']['entity_id']) sets the URL key to the same as the Name field, instead of taking the custom URL key. and it still resets the Visibility. #baffled. on the other hand, it looks like directly writing to the db works fine.. but I would really like to solve it without that method (the script will be called once a month or so)
    – Robin
    Sep 22, 2015 at 10:12
2

This one seems to do the trick pretty well:

$from = $_POST['from_group'];
$to = $_POST['to_group'];

$collection = Mage::getModel('catalog/product')->getCollection()
    ->addAttributeToSelect('*')
;

foreach($collection as $product) {
    $groupPrices = $product->getData('group_price');

    if (is_null($groupPrices)) {
        $attribute = $product->getResource()->getAttribute('group_price');
        if ($attribute) {
            $attribute->getBackend()->afterLoad($product);
            $groupPrices = $product->getData('group_price');
        }
    }

    if(empty($groupPrices) || !is_array($groupPrices)){
        continue;
    }

    $newGroupPrices = array();
    $hasChanges = false;
    foreach($groupPrices as $value)
    {

        //skip the current price for the to group
        if($value['cust_group'] == $to){
            continue;
        }

        //add the current price item
        $newGroupPrices[] = $value;

        //add new price for the to group by copying from the from group
        if($value['cust_group'] == $from){
            $newValue = $value;
            $newValue['cust_group'] = $to;
            $hasChanges = true;
            $newGroupPrices[] = $newValue;
        }

    }

    if(!$hasChanges){
        continue;
    }


    //delete old price data
    $resource = Mage::getResourceModel('catalog/product_attribute_backend_groupprice');
    $resource->deletePriceData($product->getId());

    //save new price data
    $product->setData('group_price',$newGroupPrices)->save();
}

Saving in a collection loop shouldn't be an issue. However, the way you did it by using the iterator might have been the cause of the attribute overwrite issue you were experiencing.

Does this work for you?

6
  • Hi Milan, that code deletes all the group price. if I remove the $resource->deletePriceData() line then it will replace the groupId from $from with $to.. I need the script to copy the price from the $from group to the $to group. Also it unfortunately still empties out the URL key field and resets Visibility :(
    – Robin
    Sep 22, 2015 at 8:54
  • Hi Anja, I modified my answer so it includes ->addAttributeToSelect('*'). That way all attributes will be loaded in the collection, and the existing attributes shouldn't be overwritten Sep 22, 2015 at 23:07
  • I also made the system copy the price from the from group to the to group instead of overwriting it. Sep 22, 2015 at 23:12
  • By the way, you might indeed need to skip the deletePriceData line. I tested using Magento 1.9.2, in this version the deletePriceData part was required to prevent a MySQL duplicate key error. Sep 22, 2015 at 23:17
  • Hi Milan, thanks for the effort, but in the end calling save() is very time consuming.. so I'm gonna go with the SQL answer
    – Robin
    Sep 23, 2015 at 7:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.