I am trying to customise the admin sale order grid collection.
I have created an custom sale_order attribute store_manager. These store managers are the admin users eg: 'manager1', 'manager2'.
1) Orders are manually assigned to 'manager1' or 'manager2' -- this part is done
2) Now I am trying to set filter on sale order grid collection, so when manager logged in to there system they can only see there orders.
In Magento 1 we have sales_order_grid_collection_load_before, sales_order_grid_collection_load_after for this requirement .
Is tried to such event for Magento 2 but didn't get any success.
My Queries :
Is there any such event like (sales_order_grid_collection_load_after) in magento 1 which full fill my requirement?
Is there any other way where I can set filter to sale order grid collection?
Note : I already search for this on google but didn't get any perfect solution.
I was searching for the events sales_order_grid_collection_load_after and sales_order_grid_collection_load_before to customise the sales order grid.
My findings are, there are no such events in Magento 2. A general event core_collection_abstract_load_after or core_collection_abstract_load_before dispatch to render all of the grids in Magento 2 Admin.
We can override _renderFiltersBefore() function to add a column in sales order grid or to join table with sales_order_grid. Here are the steps:
Step 1: Specify class for sales order grid data source in app/code/Vendor/Module/etc/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
<arguments>
<argument name="collections" xsi:type="array">
<item name="sales_order_grid_data_source" xsi:type="string">Vendor\Module\Model\ResourceModel\Order\Grid\Collection</item>
</argument>
</arguments>
</type>
</config>
Step 2: Add a Collection class in app/code/Vendor/Module/Model/ResourceModel/Order/Grid.php to override _renderFiltersBefore()
<?php
namespace Vendor\Module\Model\ResourceModel\Order\Grid;
use Magento\Sales\Model\ResourceModel\Order\Grid\Collection as OriginalCollection;
use Magento\Framework\Data\Collection\Db\FetchStrategyInterface as FetchStrategy;
use Magento\Framework\Data\Collection\EntityFactoryInterface as EntityFactory;
use Magento\Framework\Event\ManagerInterface as EventManager;
use Psr\Log\LoggerInterface as Logger;
class Collection extends OriginalCollection
{
protected $_authSession;
public function __construct(
EntityFactory $entityFactory,
Logger $logger,
FetchStrategy $fetchStrategy,
EventManager $eventManager,
\Magento\Backend\Model\Auth\Session $authSession
) {
$this->_authSession = $authSession;
parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager);
}
protected function _renderFiltersBefore()
{
$user = $this->_authSession->getUser();
$joinTable = $this->getTable('your_table');
$this->getSelect()->joinLeft($joinTable, 'main_table.entity_id = your_table.order_id', ['user_id']);
parent::_renderFiltersBefore();
}
}
Step 3 - Optional: To show a new column of user id in Sales order grid, modify standard grid component in app/code/Vendor/Module/view/adminhtml/ui_component/sales_order_grid.xml
<?xml version="1.0" encoding="UTF-8"?>
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
<columns name="sales_order_columns">
<column name="user_id">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">text</item>
<item name="label" xsi:type="string" translate="true">User ID</item>
</item>
</argument>
</column>
</columns>
</listing>
A reference is here
For our customized Grid, we’ll change the default data source with our own. Its parent is the standard class of the grid collection, which will let the third-party extensions effortlessly process modifications (if needed) using observers and plugins.
To implement that, in the di.xml file of our module, specify that another class should be used for the sales_order_grid data source.
app/code/Vendor/ExtendGrid/etc/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
<arguments>
<argument name="collections" xsi:type="array">
<item name="sales_order_grid_data_source" xsi:type="string">Vendor\ExtendGrid\Model\ResourceModel\Order\Grid\Collection</item>
</argument>
</arguments>
</type>
</config>
As you can see, we’ll need an extension class for the collection. Let’s create it here:
app/code/Vendor/ExtendGrid/Model/ResourceModel/Order/Grid/Collection.php
<?php
namespace Vendor\ExtendGrid\Model\ResourceModel\Order\Grid;
use Magento\Sales\Model\ResourceModel\Order\Grid\Collection as OriginalCollection;
use Vendor\ExtendGrid\Helper\Data as Helper;
/**
* Order grid extended collection
*/
class Collection extends OriginalCollection
{
}
We modify the method _renderFiltersBefore so that it could connect to our table and select the necessary column there.
To do that,
<?php
namespace Vendor\ExtendGrid\Model\ResourceModel\Order\Grid;
use Magento\Sales\Model\ResourceModel\Order\Grid\Collection as OriginalCollection;
use Vendor\ExtendGrid\Helper\Data as Helper;
/**
* Order grid extended collection
*/
class Collection extends OriginalCollection
{
protected function _renderFiltersBefore()
{
$joinTable = $this->getTable('sales_order');
$this->getSelect()->joinLeft($joinTable, 'main_table.entity_id = sales_order.entity_id', ['coupon_code']);
//or filter in sale_order_grid collection then use like that
$this->getSelect()->where("main_table.your_attribute is NULL");
parent::_renderFiltersBefore();
}
}
We usually need to execute these commands after any di.xml changes:
php -dmemory_limit=6G /[MAGENTO2_ROOT]/bin/magento setup:di:compile
php -f /[MAGENTO2_ROOT]/bin/magento setup:upgrade
rm -rf /[MAGENTO2_ROOT]/var/view_preprocessed/ /[MAGENTO2_ROOT]/var/cache/ /[MAGENTO2_ROOT]/var/page_cache/ /[MAGENTO2_ROOT]/var/generation/
rm -rf /[MAGENTO2_ROOT]/pub/static/frontend/
php -dmemory_limit=6G /[MAGENTO2_ROOT]/bin/magento setup:static-content:deploy
php -f /[MAGENTO2_ROOT]/bin/magento cache:flush
php -f /[MAGENTO2_ROOT]/bin/magento cache:clean
sudo chmod -R 777 /[MAGENTO2_ROOT]/var /[MAGENTO2_ROOT]/pub
Use these as per your requirements.
This might be late but below is working and tested solution with Magento-2.3 for adding any filter to sales order grid.
Step 1: override Magento/Sales/view/adminhtml/ui_component/sales_order_grid.xml to Vendor/Module/view/adminhtml/ui_component/sales_order_grid.xml and update dataProvider class(in overriden sales_order_grid.xml file) with your custom class say Vendor/Module/Ui/Component/DataProvider/MyDataProvider.php.
Step 2: Add following code to it
<?php
namespace Vendor\Module\Ui\Component\DataProvider;
use Magento\Framework\Api\FilterBuilder;
use Magento\Framework\Api\Search\ReportingInterface;
use Magento\Framework\Api\Search\SearchCriteriaBuilder;
use Magento\Framework\App\RequestInterface;
class MyDataProvider extends \Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider
{
public function __construct(
$name,
$primaryFieldName,
$requestFieldName,
ReportingInterface $reporting,
SearchCriteriaBuilder $searchCriteriaBuilder,
RequestInterface $request,
FilterBuilder $filterBuilder,
array $meta = [],
array $data = []
) {
parent::__construct($name, $primaryFieldName, $requestFieldName, $reporting, $searchCriteriaBuilder, $request, $filterBuilder, $meta, $data);
$this->myDefaultFilter();
}
protected function myDefaultFilter()
{
//do your stuff here get admin user id and apply filter accordingly
//here is sample filtering
$this->addFilter(
$this->filterBuilder->setField('my_column_name')->setValue('value_to_be_filtered')->setConditionType('eq')->create()
);
}
}
Now in this myDefaultFilter function call your block to get current user detail and add logic accordingly to filter grid data.
Related
I want to get 3 Fields from user to be filled with custom size and after that based on that size need to generate a price against it. That price I can show on page but when adding to Cart, It's original price of the product
I used This code with Observer method
class CustomPrice implements ObserverInterface
{
public function execute(\Magento\Framework\Event\Observer $observer) {
$item=$observer->getEvent()->getData('quote_item');
$product=$observer->getEvent()->getData('product');
// here i am using item's product final price
$price = 1000; // 10 is custom price. It will increase in product price.
// Set the custom price
$item->setCustomPrice($price);
$item->setOriginalCustomPrice($price);
// Enable super mode on the product.
$item->getProduct()->setIsSuperMode(true);
return $this;
}
}
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="checkout_cart_update_items_after">
<observer name="customprice" instance="MGS\CartPrice\Observer\CustomPrice" />
</event>
I adopted code from https://webkul.com/blog/magento2-set-custom-price-of-product/
I am trying to add an extension attribute to a quote through a custom REST API, and I would like to retrieve this info later.
But when I try to get the value, is always null.
The code will explain better than me.
etc/di.xml
<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
[...]
<preference for="Wow\Orderplatform\Api\Data\CartInterface"
type="Wow\Orderplatform\Model\Cart" />
</config>
etc/extension_attributes.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
<extension_attributes for="Magento\Quote\Api\Data\CartInterface">
<attribute code="order_platform" type="Wow\Orderplatform\Api\Data\CartInterface"/>
</extension_attributes>
</config>
CartInterface
interface CartInterface extends ExtensibleDataInterface
{
const ORDER_PLATFORM = 'order_platform';
/**
* Retrieve order platform
*
* #return string
*/
public function getOrderPlatform();
/**
* Set order platform
*
* #param string $orderPlatform
* #return $this
*/
public function setOrderPlatform($orderPlatform);
}
The implementation:
class Cart extends AbstractExtensibleModel implements CartInterface
{
/**
* {inheritdoc}
*/
public function getOrderPlatform()
{
return $this->getData(self::ORDER_PLATFORM);
}
/**
* {inheritdoc}
*/
public function setOrderPlatform($orderPlatform)
{
return $this->setData(self::ORDER_PLATFORM, $orderPlatform);
}
}
I try to save the extension attribute in a REST API:
$quote = $this->quoteRepository->getActive($cartId);
$extensions = $quote->getExtensionAttributes();
$platformExt = $this->wowCartExtInterfaceFactory->create();
$platformExt->setOrderPlatform($platform);
$extensions->setOrderPlatform($platformExt);
$quote->setExtensionAttributes($extensions);
$this->quoteRepository->save($quote);
No errors in this procedure.
The problem is that I am not able to get back the value from the quote:
$quote = $this->quoteRepository->getActive($cartId);
$extensions = $quote->getExtensionAttributes();
$extensions->getOrderPlatform(); // Always null
Any hints?
Thanks
This is core bug in magento2 tried to do same for some requirement and found
Join extension attributes are not adding to quote results
There seems to be an issue with join extension attributes as per my debugging
ex.
<extension_attributes for="Magento\Quote\Api\Data\CartItemInterface">
<attribute code="original_catalog_price" type="Magento\NegotiableQuote\Api\Data\NegotiableQuoteItemInterface" >
<join reference_table="negotiable_quote_item" reference_field="quote_item_id" join_on_field="item_id">
<field>original_catalog_price</field>
</join>
</attribute>
</extension_attributes>
Hopefully, the patch will release soon.
i'm trying to get data from multiple tables in liferay 6.0.6 using custom sql, but for now i'm just able to display data from one table.does any one know how to do that.thanks
UPDATE:
i did found this link http://www.liferaysavvy.com/2013/02/getting-data-from-multiple-tables-in.html but for me it's not working because it gives an error BeanLocator is null,and it seems that it's a bug in liferay 6.0.6
The following technique also works with liferay 6.2-ga1.
We will consider we are in the portlet project fooproject.
Let's say you have two tables: article, and author. Here are the entities in your service.xml :
<entity name="Article" local-service="true">
<column name="id_article" type="long" primary="true" />
<column name="id_author" type="long" />
<column name="title" type="String" />
<column name="content" type="String" />
<column name="writing_date" type="Date" />
</entity>
<entity name="Author" local-service="true">
<column name="id_author" type="long" primary="true" />
<column name="full_name" type="String" />
</entity>
At that point run the service builder to generate the persistence and service layers.
You have to use custom SQL queries as described by Liferay's Documentation to fetch info from multiple databases.
Here is the code of your fooproject-portlet/src/main/ressources/default.xml :
<?xml version="1.0"?>
<custom-sql>
<sql file="custom-sql/full_article.xml" />
</custom-sql>
And the custom request in the fooproject-portlet/src/main/ressources/full_article.xml:
<?xml version="1.0"?>
<custom-sql>
<sql
id="com.myCompany.fooproject.service.persistence.ArticleFinder.findByAuthor">
<![CDATA[
SELECT
Author.full_name AS author_name
Article.title AS article_title,
Article.content AS article_content
Article.writing_date AS writing_date
FROM
fooproject_Article AS Article
INNER JOIN
fooproject_Author AS Author
ON Article.id_author=Author.id_author
WHERE
author_name LIKE ?
]]>
</sql>
</custom-sql>
As you can see, we want to fetch author's name, article's title, article's content and article's date.
So let's allow the service builder to generate a bean that can store all these informations. How ? By adding it to the service.xml ! Be careful: the fields of the bean and the fields' name returned by the query must match.
<entity name="ArticleBean">
<column name="author_name" type="String" primary="true" />
<column name="article_title" type="String" primary="true" />
<column name="article_content" type="String" />
<column name="article_date" type="Date" />
</entity>
Note: defining which field is primary here does not really matter as there will never be anything in the ArticleBean table. It is all about not having exceptions thrown by the service builder while generating the Bean.
The finder method must be implemented then. To do so, create the class com.myCompany.fooproject.service.persistence.impl.ArticleFinderImpl. Populate it with the following content:
public class ArticleFinderImpl extends BasePersistenceImpl<Article> {
}
Use the correct import statements and run the service builder. Let's make that class implement the interface generated by the service builder:
public class ArticleFinderImpl extends BasePersistenceImpl<Article> implements ArticleFinder {
}
And populate it with the actual finder implementation:
public class ArticleFinderImpl extends BasePersistenceImpl<Article> implements ArticleFinder {
// Query id according to liferay's query naming convention
public static final String FIND_BY_AUTHOR = ArticleFinder.class.getName() + ".findByAuthor";
public List<Article> findByAuthor(String author) {
Session session = null;
try {
session = openSession();
// Retrieve query
String sql = CustomSQLUtil.get(FIND_BY_AUTHOR);
SQLQuery q = session.createSQLQuery(sql);
q.setCacheable(false);
// Set the expected output type
q.addEntity("StaffBean", StaffBeanImpl.class);
// Binding arguments to query
QueryPos qpos = QueryPos.getInstance(q);
qpos.add(author);
// Fetching all elements and returning them as a list
return (List<StaffBean>) QueryUtil.list(q, getDialect(), QueryUtil.ALL_POS, QueryUtil.ALL_POS);
} catch(Exception e) {
e.printStackTrace();
} finally {
closeSession(session);
}
return null;
}
}
You can then call this method from your ArticleServiceImpl, whether it is to make a local or a remote API.
Note: it is hack. This is not a perfectly clean way to retrieve data, but it is the "less bad" you can do if you want to use Liferay's Service Builder.
I need to know how to add a data range (from and a to date) for tier prices in magento, which will make the prices only appear within the given date range.
I am new to magento, so any guidance will be great help.
thanks in advance.
Since this is not possible by default within, magento what you could try is :
Create 2 fields in Admin -> Catalog -> Attribute for tierprice_to_date and tierprice_from_date and add it to price group in your Attribute Sets.
In /app/design/frontend/base/default/template/catalog/product/view.phtml
if(date between tierprice_from_date and tierprice_to_date){
echo $this->getTierPriceHtml();
}
Then create a custom module with observer that check the price when items are added to cart using event 'sales_quote_add_item':
Create: app/code/local/MageIgniter/TierPriceDateRange/etc/config.xml
<?xml version="1.0"?>
<config>
<modules>
<MageIgniter_TierPriceDateRange>
<version>1.0.1</version>
</MageIgniter_TierPriceDateRange>
</modules>
<global>
<models>
<tierpricedaterange>
<class>MageIgniter_TierPriceDateRange_Model</class>
</tierpricedaterange>
</models>
<events>
<sales_quote_add_item>
<observers>
<tierpricedaterange_observer>
<type>singleton</type>
<class>tierpricedaterange/observer</class>
<method>updatePrice</method>
</tierpricedaterange_observer>
</observers>
</sales_quote_add_item>
</events>
</global>
</config>
Create: app/code/local/MageIgniter/TierPriceDateRange/Model/Observer.php
class MageIgniter_TierPriceDateRange_Model_Observer
{
public function updatePrice($observer) {
if(date NOT between tierprice_from_date and tierprice_to_date){
$cartItem = $observer->getEvent()->getQuoteItem();
// check if a tier price was apply and change it back to the original price (none tier price)
$product = Mage::getModule('catalog/product')->load($product->getId());
if($cartItem->getPrice() == $product->getTierPrice($cartItem->getQty())){
$new_price = $product->getPrice();
$product->setOriginalCustomPrice($new_price);
$product->save();
}
}
return $this;
}
Create: app/etc/modules/MageIgniter_TierPriceDateRange.xml
<?xml version="1.0"?>
<config>
<modules>
<MageIgniter_TierPriceDateRange>
<active>true</active>
<codePool>local</codePool>
</MageIgniter_TierPriceDateRange>
</modules>
</config>
Then clear cache, if any.
This is not possible within the standard Magento configuration. You would have to build (or have it build for you) a custom module to make this possible.
See also http://www.magentocommerce.com/boards/viewthread/230679/
I wanted to deserialize an XML message containing an element that can be marked nil="true" into a class with a property of type int?. The only way I could get it to work was to write my own NullableInt type which implements IXmlSerializable. Is there a better way to do it?
I wrote up the full problem and the way I solved it on my blog.
I think you need to prefix the nil="true" with a namespace in order for XmlSerializer to deserialise to null.
MSDN on xsi:nil
<?xml version="1.0" encoding="UTF-8"?>
<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="array">
<entity>
<id xsi:type="integer">1</id>
<name>Foo</name>
<parent-id xsi:type="integer" xsi:nil="true"/>
My fix is to pre-process the nodes, fixing any "nil" attributes:
public static void FixNilAttributeName(this XmlNode #this)
{
XmlAttribute nilAttribute = #this.Attributes["nil"];
if (nilAttribute == null)
{
return;
}
XmlAttribute newNil = #this.OwnerDocument.CreateAttribute("xsi", "nil", "http://www.w3.org/2001/XMLSchema-instance");
newNil.Value = nilAttribute.Value;
#this.Attributes.Remove(nilAttribute);
#this.Attributes.Append(newNil);
}
I couple this with a recursive search for child nodes, so that for any given XmlNode (or XmlDocument), I can issue a single call before deserialization. If you want to keep the original in-memory structure unmodified, work with a Clone() of the XmlNode.
The exceptionally lazy way to do it. It's fragile for a number of reasons but my XML is simple enough to warrant such a quick and dirty fix.
xmlStr = Regex.Replace(xmlStr, "nil=\"true\"", "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:nil=\"true\"");