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' => '===='));
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.

  • Hi Marius, can you show me an example where/how to call $product->load() in my code? – Robin Sep 22 '15 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 '15 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 '15 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 '15 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 '15 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?

  • 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 '15 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 – Milan Simek Sep 22 '15 at 23:07
  • I also made the system copy the price from the from group to the to group instead of overwriting it. – Milan Simek Sep 22 '15 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. – Milan Simek Sep 22 '15 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 '15 at 7:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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