Magento: Add column in sales order grid without join - php

I need to add a custom column in the sales order adminhtml grid.
Since I get the data for that grid from an external REST API by asking for data to a specific increment_id, I can't use the tutorials which are using a magento database to join the needed tables.
Is there another way like:
function rendering_sales_order_row_before($rowdata) {
$columnContent = $restapi->callByIncrementId($rowdata['increment_id']);
$this->addColumn("Custom Column", $columnContent);
}
(This code should just illustrate my goal and I know that the solution will look completely different)
Is it possible to achieve that in magento in an elegant way?
EDIT: I'm using magento 1.9.2.1

Good question. I had a similiar problem. I solved it by using a custom renderer for my column.
First add your Sales Order Grid block XX_ModuleName_Block_Adminhtml_Order_Grid. Rewrite Mage_Adminhtml_Block_Sales_Order_Grid, extend it and add your column by overriding _prepareColumns() method.
$this->addColumn('my_column', array(
'header' => Mage::helper('sales')->__('My Column'),
'width' => '80px',
'index' => 'my_column',
'type' => 'text',
'order' => 'x',
));
Then add a custom renderer (XX_ModuleName_Block_Adminhtml_Sales_Order_Grid_Widget_Renderer_MyColumn) and extend Mage_Adminhtml_Block_Widget_Grid_Column_Renderer_Abstract.
Override the render(Varien_Object $row) method. Here you can do your specific operation (do your api request, manipulate in whatever way you want) and return the string. To build your request you want to use the data by param $row.
That should work :) If you need more info, just comment.

Related

How to make referenced column sortable in drupal 7 view

I have one view in Drupal 7, which displays user information like (Name, address, Status, etc..). I have one column in this (Table)view as "Published event". Basically events are created by users do I want to make this column sortable. I have attached image for more reference.
I tried with applying relationship but no success.
table settings
my handler code is like below :
$handler->display->display_options['sorts']['event_count_published'] ['id'] = 'event_count_published';
$handler->display->display_options['sorts']['event_count_published'] ['table'] = 'search_api_index_user_search_index';
$handler->display->display_options['sorts']['event_count_published'] ['field'] = 'event_count_published';
$handler->display->display_options['sorts']['event_count_published'] ['order'] = 'DESC';
'mail' => array(
'sortable' => 1,
'default_sort_order' => 'asc',
'align' => '',
'separator' => '',
'empty_column' => 0,
),
'event_count_published' => array(
'align' => '',
'separator' => '',
'empty_column' => 0,
'sortable' => 1,
),
above code is in "tcd_reporting.views_default.inc" file, if I put 'sortable => 1', it still does not provide sorting
field is created by below code:
$properties['event_count_published'] = array(
'label' => t('Published Events'),
'description' => t('Number of published events authored by user.'),
'type' => 'integer',
'getter callback' => 'tcd_event_content_type_count_published_get',
'computed' => TRUE,
'entity views field' => TRUE,
);
[Introduction] Which function is responsible for 'click sort' in views?
Click sort -this checkbox from your second screen- in views table settings is function which is enabled only for fields which have properly defined handlers. As you may know each field in views have few handlers (for displaying, filtering, sorting). And for click sort to be possible on specified column its field handler must have two functions defined: click_sortable and click_sort. First one just need to return true, while second need to properly implements sorting on view. For example see handler: [views_module_path]/handlers/views_handler_field.inc.
Your case:
It seems that your column "Published event" have defined handler which does not have click_sortable and click_sort functions (or click_sortable simply returns false).
Possible fix:
Find place where you defined your view source (it depends on how you informed views about it, if I understand its something like "User info" - maybe in hook_entity_info function or hook_views_data function), check what handler is assigned to your "Published event" field and change it.
It's hard to tell where you need to look as it depends on your implementation.
I suggest you to try create hook_views_data_alter function and dpm() it for start. Later you can alter it like that:
mymodule_views_data_alter(&$data) {
$data['some_view_info']['published_event']['field']['handler'] = 'views_handler_field_numeric';
}
Edit 1
First could you tell where this code is? Is it inside handler class, or maybe some views hook? Views gives you a lot of flexibility but this make them hard to understand, and I'm not sure what exactly you achieve and how.
Assuming your field works properly you can try to simply enable click sort.
Example: I created hook_views_data_alter function to see content of views data
function mymodule_views_data_alter(&$data) {
dpm($data,'d');
}
You might need to clear cache to see dpm of *_alter hooks.
Inside dpm'ed array I found "users" for generic example, and its field name looks like this:
I suggest you to try alter your field with click_sortable = TRUE and see what happens. If this wont help please provide more information about your field, how you created it, how it looks in hook_views_data_alter and which handlers it has defined.
Edit 2
Ok, so you have your views exported to code into views_default file. But this only allows you to export view you created from database to code, so it is basically a reflection of what you done in views web editor (eg. page yourwebsite.com/admin/structure/views/view/your_view_name/edit). What you need to do is to change behavior of one of your fields so it became sortable (add click_sortable and click_sort functions in handler class) or change handler of this field to one with sorting option (change field handler to other one like views_handler_field_numeric). If you don't have experience in creating handlers and this is one of generic handlers i suggest you to go back to my Edit 1, examine your dpm, and try to alter $data array to find solution.
Edit 3
Little explanation to prevent confusion. When creating new view you select collection on which this particular view base on (simpliest example - it may be MySQL table, and view will use SQL queries to retrieve data from it). By digging down we have:
Collection - eg. User which is database table user, it is what you select as source when creating new view.
Field - eg. mail which is database column mail, this fields you add to your view.
Field handler - eg. views_handler_field_numeric, this is class name of handler to use by specified field
Now, if you don't created your own handler then your field "Published event" have one of generic views handler. You shouldn't ever change code of contributed modules - especially so widely used as views handlers. That's why my suggestion to add functions click_sortable and click_sort is incorrect. Instead you should change handler responsible for field "Published event".
Best way is to define proper handler in place where you define your field "Published event". If it's somehow impossible the only way I can think of is hook_views_data_alter see docs for more info and examples. I suppose you should try to redefine handler of your field to generic numeric handler views_handler_field_numeric as it should have full sorting functionallity, or try to add click_sortable property to field array as you can see in first image of my post, but I can't provide you fully tested example.

Magento - Differentiate Normal orders and orders created programatically

I am able to create orders programmatically using my custom module.
I wish to add a flag to differentiate between various types of orders that can be created from admin/websites and orders created from my custom module.
I understand that admin and website orders can be differentiated by
if(!empty($order->getRemoteIp()){
//place online
}
else{
// place by admin
}
However, I still want to differentiate between orders manually placed from admin and orders placed from my custom module.
The following are some solutions I thought of
1) Adding a prefix to the order or order-increment id.
2) Creating a new store during creation of module and add all orders from my custom module using that store. However, I am not sure of what implications this might cause.
3) I am able to change the store name during order creation using
$order->setStoreName('customName');
But, this is not visible in the admin grid or order detail page. I'm guessing they fetch the information for "Purchased From" from the store id.
I am looking for, what could be the best solution from the above, or a better solution if any.
Note: My module is currently compatible with magento v1.4 and above. So I will need a solution that covers most versions.
You could create an order attribute and set it when you create your order.
To create an order attribute you can do like this, in your module folder, in sql/mymodule_setup/mysql4-upgrade-VNUMBER.php:
$installer = $this;
$installer->startSetup ();
$setup = new Mage_Eav_Model_Entity_Setup ( 'core_setup' );
$installer->getConnection()
->addColumn (
$installer->getTable ( 'sales_flat_order' ),
'my_attribute_code', // this is where you set the attribute code
'varchar(255) DEFAULT NULL' );
$setup->startSetup ();
$setup->addAttribute (
'order',
'my_attribute_code',
array (
'type' => 'int', // or text or whatever
'label' => 'My attribute Label'
));
$installer->endSetup ();
Then when dealing with the order, simply use
$order->setMyAttributeCode($value);

How to display model data in various formats?

I am changing the data provided by my model in the afterFind() method, so the id is clickable text, like this:
$this->id = CHtml::link($this->id, array('/admin/auditTrail/view', 'id' => $this->id));
However, this changes every single occurrence of id value - it is in lists, in detail page, even in breadcrumbs, which is unwilling of course. How can I decide which format I will use in different views? For example, in breadcrumbs and in view.php I just want the raw value, but in the list (admin.php) I would like to use the html link, like this:
'columns'=>array(
array(
'name' => 'id',
'type' => 'html',
),
On a separate note - is this a good approach in terms of MVC, I mean changing display in a model? Should not be model only used for a database manipulation stuff?
No, this is not a good approach. There are a number of alternative approaches that would be better, for example:
Let the view do the conversion
Don't do anything in the model. When the view wants to display clickable anchors instead of bare ids, it should generate the URLs itself. This is somewhat quick and dirty (it puts the logic of URL generation in the view, which is not ideal) but it's easy and it works well if you only need to do it in one or two places.
Expose the URL as a separate property
Place a calculated read-only property in your model:
public function getAuditTrailUrl()
{
return Yii::app()->createUrl('/admin/auditTrail/view',
array('id' => $this->id));
}
You can then use the auditTrailUrl property in any view. The nice thing about this approach is that the URL generation is opaque to the views and therefore easily modifiable.
You can use this syntax to easily render links from these URLs in your CDataGrid:
'columns' => array(
array(
'class' => 'CLinkColumn',
'urlExpression' => '$data->auditTrailUrl',
)
),
In the above definition $data refers to each model in the grid, as per the documentation.

Magento basic advice needed: modifying a specific product grid in the admin section?

I have a few general questions about modifying Magento's admin section and would be grateful to have them answered. I'm new to Magento so please bear with me.
My goal is to add a new column with a product attribute (e.g. "Size") to the "Category Products" table within the Catalog -> Manage Cateories section (see screenshot below).
Having little Magento dev experience, I'm not quite sure where to start. I had a look in some of the Grid.php files under the adminhtml directory, and while I see a bunch of statements like addColumn(...), I'm not sure where I'd slot in my new attribute column.
Also, I assume that instead of modifying any core files directly, I'd copy them to the same path under the local folder and edit or somehow extend them there? Do I have to edit any config files or do anything else for the change to be reflected? Am I - by doing this - in effect creating my own module?
I also read that I should disable "Compilation" before I make any changes. Why is this? Is there anything else to consider?
Again I am very grateful for any help and appreciate that my questions must seem basic. Any supplementary resources you could point me towards would be appreciated. Thanks.
Indeed you should start by understanding what file to edit and how to edit it. In this case you want to modify app/code/core/Mage/Adminhtml/Block/Catalog/Category/Tab/Product.php but, like you said, you should not modify the file in its current location. There are two ways to modify the file in the "correct" way.
(harder but more extensible) Create a new Module in local and tell Magento in the etc/config.xml that you are overwriting that Block (which is just a php class) with a different block in this new Module and have the new class extend the core Block class. Then you just need to overwrite one function (_prepareColumns).
(easier) Copy the file from app/code/core/Mage/Adminhtml/Block/Catalog/Category/Tab/Product.php to app/code/local/Mage/Adminhtml/Block/Catalog/Category/Tab/Product.php and modify the function you want (_prepareColumns)
If you are new to Magento, I recommend going with the second option because its easier. Magento will always load the file from local before it loads from core so the file in core will no longer be used and your version in local will be used. To find out more, read this article from Alan Storm
Now in order to add the column you want, do something similar to the SKU field
$this->addColumn('size', array(
'header' => Mage::helper('catalog')->__('Size'),
'index' => 'size'
));
in the order you want it (between Product Name and SKU). I am assuming that your Products have a field called size that you can retreive with $product->getSize()
Max solution was pretty spot on but missing some important steps, I'll elaborate on his original method
Create a new local override of the Product Tab by copying app/code/core/Mage/Adminhtml/Block/Catalog/Category/Tab/Product.php to app/code/local/Mage/Adminhtml/Block/Catalog/Category/Tab/Product.php
There are 2 functions involved in modifying the grid view. _prepareCollection and _prepareColumns
_prepareColumns by adding a call to the addColumn function just like:
$this->addColumn('size', array(
'header' => Mage::helper('catalog')->__('Size'),
'width' => '80',
'index' => 'size'
));
_prepareCollection, by default the product collection loaded in the grid only has a few attributes(name,sku,price) what you need to do add our now attribute by ->addAttributeToSelect('size') now if you are only working with a textfield attribute then this is the extend of the modifications you have to do however if your attribute is for example a dropdown you will need to do further changes to the prepare collection:
(optional) dropdown attributes only store the value of the option that was select so we need to provide an options array to the addColumns call so Magento can display the values correctly, we can do that in the following maner:
on your local copy of Products, add the following to the _prepareColumns functions
$attribute = Mage::getModel('eav/config')->getAttribute('catalog_product', 'colour');
$options = array();
foreach( $attribute->getSource()->getAllOptions(true, true) as $option ) {
$options[$option['value']] = $option['label'];
}
$this->addColumn('colour', array(
'header' => Mage::helper('catalog')->__('Colour'),
'width' => '80',
'index' => 'colour',
'type' => 'options',
'options' => $options
));
While those are some very thorough question and I'm sure you will learn a lot, there is a ready-made solution; Enhanced Admin Product Grid has the ability to add arbitrary attributes as columns.

Getting list of products by category in Magento using SOAP-based API

I need to get all products belonging to a specific category in Magento using the web services API. I tried this method:
$product_filter = array(
'category_ids' => array('eq' => '41')
);
$product_templates = $magento_client -> call($magento_session, 'product.list');
But it returns an error. I can only assume it's because category_ids is an array, so it won't really ever equal one specific value.
I did some research and found another method called category.assignedProducts and tried:
$product_templates =
$magento_client ->
call($magento_session, 'catalog_category.assignedProducts', array('41'));
But this returned an 'Access Denied' error. I went and looked at my sandbox of Magneto and saw that 'Assigned Products' has 3 options: 'Remove', 'Update', 'Assign', and I know that the admin for the system I'm linking to has set my access to 'Read-Only'. So I'm guessing that we'd have to check off 'assign' in that list, which would give me more access than they want to give.
I could retrieve all of the data and do the filtering on my end, but I wanted to check if anyone knew of a better way.
Thanks.
assignedProducts sounds like what you need but you shouldn't need to be passing along an array but an integer value and the store ID or code.
See the arguments required: http://www.magentocommerce.com/wiki/doc/webservices-api/api/catalog_category#catalog_category.assignedproducts
I think I found the answer on http://www.magentocommerce.com/boards/viewthread/207099/ which basically says it can't be done from the product any more. We must now look at the problem from the category point of view.
category_ids no longer works in 1.4, they changed the table
structures around so that the categories are not available on the
product. Use the code below to get the products from the category and
then do a catalog_product.list call. To make it faster you can also
create a custom api solution to combine these in Magento and perform
just one call instead of two.
$proxy = new SoapClient($soapUrl.’api/soap/?wsdl’); $sessionId =
$proxy->login($apiUser, $apiPass);
$productList = $proxy->call($sessionId,
‘catalog_category.assignedProducts’, array(’4’));
$proxy->endSession($sessionId); print_r($productList);

Categories