Category: Magento2

Generate invoice and shipment automatically when a new order is placed in Magento 2

Many merchants wants to create invoice / shipment and complete the order all at once when place order. So today I will discuss about how achieve their need. Although it’s custom needs/requirement so we need to create a custom module for that.

In that case, you need to do is to observe the checkout_submit_all_after event. So lets start.

Here Vendor is ‘SR’ and Module name ‘AutoInvoiceShipment’.

So create a module you can follow this post

Create events.xml [SR/AutoInvoiceShipment/etc/events.xml]

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="checkout_submit_all_after">
        <observer name="sr_auto_invoice_shipment_checkout_submit_all_after" instance="SR\AutoInvoiceShipment\Observer\CheckoutAllSubmitAfterObserver"/>
    </event>
</config>

Create observer class for that [SR/AutoInvoiceShipment/Observer/CheckoutAllSubmitAfterObserver.php]

namespace SR\AutoInvoiceShipment\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;

class CheckoutAllSubmitAfterObserver implements ObserverInterface
{
    /**
     *
     * @var \SR\AutoInvoiceShipment\Helper\Data
     */
    protected $helper;

    /**
     * @param \SR\AutoInvoiceShipment\Helper\Data $helper
     */
    public function __construct(
        \SR\AutoInvoiceShipment\Helper\Data $helper
    ) {
        $this->helper = $helper;
    }

    /**
     *
     * @param Observer $observer
     * @return $this
     */
    public function execute(Observer $observer)
    {
        if(!$this->helper->isEnabled()) {
            return $this;
        }

        $order = $observer->getEvent()->getOrder();
        if(!$order->getId()) {
            return $this;
        }

        $invoice = $this->helper->createInvoice($order);
        if($invoice) {
            $this->helper->createShipment($order, $invoice);
        }

        return $this;
    }
}

Create a helper[SR/AutoInvoiceShipment/Helper/Data.php]

namespace SR\AutoInvoiceShipment\Helper;

use Magento\Framework\App\Helper\AbstractHelper;

class Data extends AbstractHelper
{
    const MODULE_ENABLED = 'sr_auto_invoice_shipment/settings/enabled';

    /**
     * @var \Magento\Framework\App\Config\ScopeConfigInterface
     */
    protected $scopeConfig;

    /**
     *
     * @var \Magento\Sales\Model\ResourceModel\Order\Invoice\CollectionFactory
     */
    protected $invoiceCollectionFactory;

    /**
     *
     * @var \Magento\Sales\Model\Service\InvoiceService
     */
    protected $invoiceService;

    /**
     *
     * @var \Magento\Sales\Model\Order\ShipmentFactory
     */
    protected $shipmentFactory;

    /**
     *
     * @var \Magento\Framework\DB\TransactionFactory
     */
    protected $transactionFactory;

    /**
     * @param \Magento\Framework\App\Helper\Context $context
     * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
     * @param \Magento\Sales\Model\ResourceModel\Order\Invoice\CollectionFactory $invoiceCollectionFactory
     * @param \Magento\Sales\Model\Service\InvoiceService $invoiceService
     * @param \Magento\Sales\Model\Order\ShipmentFactory $shipmentFactory
     * @param \Magento\Framework\DB\TransactionFactory $transactionFactory
     */
    public function __construct(
        \Magento\Framework\App\Helper\Context $context,
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
        \Magento\Sales\Model\ResourceModel\Order\Invoice\CollectionFactory $invoiceCollectionFactory,
        \Magento\Sales\Model\Service\InvoiceService $invoiceService,
        \Magento\Sales\Model\Order\ShipmentFactory $shipmentFactory,
        \Magento\Framework\DB\TransactionFactory $transactionFactory
    ) {
        parent::__construct($context);
        $this->scopeConfig = $scopeConfig;
        $this->invoiceCollectionFactory = $invoiceCollectionFactory;
        $this->invoiceService = $invoiceService;
        $this->shipmentFactory = $shipmentFactory;
        $this->transactionFactory = $transactionFactory;
    }

    public function createInvoice($order)
    {
        try {
            $invoices = $this->invoiceCollectionFactory->create()
                ->addAttributeToFilter('order_id', array('eq' => $order->getId()));

            $invoices->getSelect()->limit(1);

            if ((int)$invoices->count() !== 0) {
                return null;
            }

            if(!$order->canInvoice()) {
                return null;
            }

            $invoice = $this->invoiceService->prepareInvoice($order);
            $invoice->setRequestedCaptureCase(\Magento\Sales\Model\Order\Invoice::CAPTURE_OFFLINE);
            $invoice->register();
            $invoice->getOrder()->setCustomerNoteNotify(false);
            $invoice->getOrder()->setIsInProcess(true);
            $order->addStatusHistoryComment('Automatically INVOICED', false);
            $transactionSave = $this->transactionFactory->create()->addObject($invoice)->addObject($invoice->getOrder());
            $transactionSave->save();
        } catch (\Exception $e) {
            $order->addStatusHistoryComment('Exception message: '.$e->getMessage(), false);
            $order->save();
            return null;
        }

        return $invoice;
    }

    public function createShipment($order, $invoice)
    {
        try {
            $shipment = $this->prepareShipment($invoice);
            if ($shipment) {
                $order->setIsInProcess(true);
                $order->addStatusHistoryComment('Automatically SHIPPED', false);
                $this->transactionFactory->create()->addObject($shipment)->addObject($shipment->getOrder())->save();
            }
        } catch (\Exception $e) {
            $order->addStatusHistoryComment('Exception message: '.$e->getMessage(), false);
            $order->save();
        }
    }

    public function prepareShipment($invoice)
    {
        $shipment = $this->shipmentFactory->create(
            $invoice->getOrder(),
            []
        );

        return $shipment->getTotalQty() ? $shipment->register() : false;
    }

    /**
     * Is the module enabled in configuration.
     *
     * @return bool
     */
    public function isEnabled()
    {
        return $this->scopeConfig->getValue(self::MODULE_ENABLED);
    }
}

That’s it.

If you want an option that you can handle this module on/off. So create a configuration
[SR/AutoInvoiceShipment/etc/adminhtml/system.xml]

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <section id="sr_auto_invoice_shipment" translate="label" type="text" sortOrder="1300" showInDefault="1" showInWebsite="1" showInStore="1">
            <label>Auto Invoice Shipment</label>
            <tab>sales</tab>
            <resource>SR_AutoInvoiceShipment::sr_auto_invoice_shipment</resource>
            <group id="settings" translate="label" sortOrder="1000" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>Extension Settings</label>
                <field id="enabled" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Enabled</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                </field>
            </group>
        </section>
    </system>
</config>

Download Full Module From Here

How to integrate recaptcha in magento 2 contact page

Today I discuss, How to integrate google reCaptcha into contact page. So you need to view captcha and validation captcha.

Step 1: Create Module.xml [SR/ReCaptcha/etc/module.xml]


<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="SR_ReCaptcha" setup_version="2.1.0">
    </module>
</config>

Step 2: Create registration.php [SR/ReCaptcha/registration.php]


\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'SR_ReCaptcha',
    __DIR__
);

Step 3: Create system configuration [SR/ReCaptcha/etc/adminhtml/system.xml]


<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <tab id="sr_reCaptcha">
            Re Captcha
        </tab>
        <section id="reCaptcha" type="text" showInDefault="1">
            <label>Re Captcha Configuration</label>
            <tab>sr_reCaptcha</tab>
            <resource>SR_ReCaptcha::reCaptcha</resource>
            <group id="settings" type="text"  sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                <label>Extension Settings</label>
                <field id="enabled" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Enabled</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                </field>
                <field id="site_key" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Site Key</label>
                </field>
                <field id="secret_key" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Secret Key</label>
                </field>
            </group>
        </section>
    </system>
</config>


Step 4: Assign default value for system configuration [SR/ReCaptcha/etc/config.xml]


<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <reCaptcha>
            <settings>
                <enabled>0</enabled>
                <site_key></site_key>
                <secret_key></secret_key>
            </settings>
        </reCaptcha>
    </default>
</config>


Step 5: Create helper [SR/ReCaptcha/Helper/Data.php]


namespace SR\ReCaptcha\Helper;

use Magento\Store\Model\ScopeInterface;

class Data extends \Magento\Framework\App\Helper\AbstractHelper
{
    const MODULE_ENABLED = 'reCaptcha/settings/enabled';
    const SITE_KEY = 'reCaptcha/settings/site_key';
    const SECRET_KEY = 'reCaptcha/settings/secret_key';

    /**
     * @var \Magento\Store\Model\StoreManagerInterface
     */
    protected $storeManager;

    /**
     * @var \Magento\Framework\App\Config\ScopeConfigInterface
     */
    protected $scopeConfig;

    /**
     * Data constructor.
     *
     * @param \Magento\Framework\App\Helper\Context $context
     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
     * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
     */
    public function __construct(
        \Magento\Framework\App\Helper\Context $context,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
    ) {
        parent::__construct($context);
        $this->storeManager = $storeManager;
        $this->scopeConfig = $scopeConfig;
    }

    /**
     * Is the module enabled in configuration.
     *
     * @return bool
     */
    public function isEnabled()
    {
        return $this->getStoreConfig(self::MODULE_ENABLED);
    }

    /**
     * The recaptcha site key.
     *
     *
     * @return string
     */
    public function getSiteKey()
    {
        return $this->getStoreConfig(self::SITE_KEY);
    }

    /**
     * The recaptcha secret key.
     *
     *
     * @return string
     */
    public function getSecretKey()
    {
        return $this->getStoreConfig(self::SECRET_KEY);
    }

    public function getStoreConfig($path)
    {
        $store = $this->getStoreId();
        return $this->scopeConfig->getValue($path, ScopeInterface::SCOPE_STORE, $store);
    }

    public function getStoreId()
    {
        return $this->storeManager->getStore()->getStoreId();
    }
}

Step 6: Layout configuration [SR/ReCaptcha/view/frontend/layout/contact_index_index.xml]


<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="form.additional.info">
            <block class="SR\ReCaptcha\Block\ReCaptcha" name="recaptchaForm" template="SR_ReCaptcha::captcha.phtml" after="-" cacheable="false" />
        </referenceContainer>
    </body>
</page>


Step 7: Create phtml file [SR/ReCaptcha/view/frontend/templates/captcha.phtml]


<?php /** @var $block \SR\ReCaptcha\Block\ReCaptcha */ ?>
<?php if ($block->isEnabled()): ?>
    https://www.google.com/recaptcha/api.js
    
getSiteKey();?>==">
<?php endif; ?>

Step 7: Create a plugin [SR/ReCaptcha/etc/frontend/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\Contact\Controller\Index\Post">
        <plugin name="reCaptcha_Post_Action" type="SR\ReCaptcha\Plugin\Contact\Controller\Index\Post" sortOrder="1"/>
    </type>
</config>


Step 8: Create a plugin class [SR/ReCaptcha/Plugin/Contact/Controller/Index/Post.php]


namespace SR\ReCaptcha\Plugin\Contact\Controller\Index;

class Post
{
    /**
     * @var \Magento\Framework\Controller\Result\RedirectFactory
     */
    protected $resultRedirectFactory;

    /**
     * @var \Magento\Framework\Message\ManagerInterface
     */
    protected $messageManager;

    /**
     * @var \SR\ReCaptcha\Helper\Data $dataHelper
     */
    protected $dataHelper;

    /**
     * @param \Magento\Framework\Controller\Result\RedirectFactory $resultRedirectFactory
     * @param \Magento\Framework\Message\ManagerInterface $messageManager
     * @param \SR\ReCaptcha\Helper\Data $dataHelper
     */
    public function __construct(
        \Magento\Framework\Controller\Result\RedirectFactory $resultRedirectFactory,
        \Magento\Framework\Message\ManagerInterface $messageManager,
        \SR\ReCaptcha\Helper\Data $dataHelper
    ) {
        $this->resultRedirectFactory = $resultRedirectFactory;
        $this->messageManager = $messageManager;
        $this->dataHelper = $dataHelper;
    }

    public function aroundExecute(
        \Magento\Contact\Controller\Index\Post $subject,
        \Closure $proceed
    ) {
        if($this->dataHelper->isEnabled()) {
            $recaptcha_response_field = $subject->getRequest()->getPost('g-recaptcha-response');
            if($recaptcha_response_field) {
                $secretKey = $this->dataHelper->getSecretKey();
                $response = file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=".$secretKey."&response=".$recaptcha_response_field."&remoteip=".$_SERVER['REMOTE_ADDR']);
                $result = json_decode($response, true);
                if(isset($result['success']) && ($result['success'])) {
                    return $proceed();
                } else {
                    $resultRedirect = $this->resultRedirectFactory->create();
                    $this->messageManager->addError(
                        __('There was an error with the recaptcha code, please try again.')
                    );
                    $resultRedirect->setPath('contact/index');
                    return $resultRedirect;
                }
            } else {
                $this->messageManager->addError(
                    __('There was an error with the recaptcha code, please try again.')
                );
                $resultRedirect = $this->resultRedirectFactory->create();
                $resultRedirect->setPath('contact/index/index');
                return $resultRedirect;
            }
        }

        return $proceed();
    }
}

Download full module from here

After installation for configuration go to Store -> configuration
re-captcha config

How to use html content in magento2 widget

Today I am going to discuss about some limitation of Magento2 widget. You can create different type of widget with different input field. Suppose you create a TextArea and you try to entry some html content here, then what happened? Ideally you can’t do in default Magento2 installation, it just breaks the edit functionality. So in that case you need some care about this. So today I build an extension for overcome this situation.

So need to overwrite widget save and load controller.

Step 1: Pluginize ‘BuildWidget’ controller and ‘LoadOptions’ controller


# SR/RewriteWidget/etc/adminhtml/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\Widget\Controller\Adminhtml\Widget\BuildWidget">
        <plugin name="rewrite_widget_build_widget" type="SR\RewriteWidget\Plugin\Widget\Controller\Adminhtml\Widget\BuildWidget" sortOrder="1"/>
    </type>
    <type name="Magento\Widget\Controller\Adminhtml\Widget\LoadOptions">
        <plugin name="rewrite_widget_load_options" type="SR\RewriteWidget\Plugin\Widget\Controller\Adminhtml\Widget\LoadOptions" sortOrder="1"/>
    </type>
</config>

Step 2: Create SR/RewriteWidget/Plugin/Widget/Controller/Adminhtml/Widget/BuildWidget.php


namespace SR\RewriteWidget\Plugin\Widget\Controller\Adminhtml\Widget;

class BuildWidget
{
    /**
     * @var \Magento\Widget\Model\Widget
     */
    protected $widget;

    /**
     * @var \SR\RewriteWidget\Helper\Data
     */
    protected $helper;

    /**
     * @param \Magento\Widget\Model\Widget $widget
     * @param \SR\RewriteWidget\Helper\Data $helper
     */
    public function __construct(
        \Magento\Widget\Model\Widget $widget,
        \SR\RewriteWidget\Helper\Data $helper
    ) {
        $this->widget = $widget;
        $this->helper = $helper;
    }

    /**
     * Format widget pseudo-code for inserting into wysiwyg editor
     *
     * @return void
     */
    public function aroundExecute(
        \Magento\Widget\Controller\Adminhtml\Widget\BuildWidget $subject,
        \Closure $proceed
    ) {
        $type = $subject->getRequest()->getPost('widget_type');
        $params = $subject->getRequest()->getPost('parameters', []);
        $asIs = $subject->getRequest()->getPost('as_is');

        if($type == 'SR\RewriteWidget\Block\Widget\Content') {

            foreach($params as $key => $value) {
                $params[$key] = $this->helper->encodeWidgetValues($value);
            }
        }

        $html = $this->widget->getWidgetDeclaration($type, $params, $asIs);
        $subject->getResponse()->setBody($html);
    }
}

Step 3: Create SR/RewriteWidget/Plugin/Widget/Controller/Adminhtml/Widget/LoadOptions.php


namespace SR\RewriteWidget\Plugin\Widget\Controller\Adminhtml\Widget;

class LoadOptions
{
    /**
     * @var \Magento\Framework\ObjectManagerInterface
     */
    protected $objectManager;

    /**
     * @var \Magento\Framework\App\ViewInterface
     */
    protected $view;


    /**
     * @param \Magento\Framework\ObjectManagerInterface $objectManager
     * @param \Magento\Framework\App\ViewInterface $view
     */
    public function __construct(
        \Magento\Framework\ObjectManagerInterface $objectManager,
        \Magento\Framework\App\ViewInterface $view
    ) {
        $this->view = $view;
        $this->objectManager = $objectManager;
    }
    /**
     * Ajax responder for loading plugin options form
     *
     * @return void
     */
    public function aroundExecute(
        \Magento\Widget\Controller\Adminhtml\Widget\LoadOptions $subject,
        \Closure $proceed
    ) {
        try {
            $this->view->loadLayout();
            if ($paramsJson = $subject->getRequest()->getParam('widget')) {
                $request = $this->objectManager->get('Magento\Framework\Json\Helper\Data')->jsonDecode($paramsJson);
                if (is_array($request)) {
                    $optionsBlock = $this->view->getLayout()->getBlock('wysiwyg_widget.options');
                    if (isset($request['widget_type'])) {
                        $optionsBlock->setWidgetType($request['widget_type']);
                    }
                    if (isset($request['values'])) {
                        if($optionsBlock->getWidgetType() == 'SR\RewriteWidget\Block\Widget\Content') {
                            $helper = $this->objectManager->create('SR\RewriteWidget\Helper\Data');
                            foreach($request['values'] as $key => $value) {
                                $request['values'][$key] = $helper->decodeWidgetValues($value)
                            }
                        }
                        $optionsBlock->setWidgetValues($request['values']);
                    }
                }
                $this->view->renderLayout();
            }
        } catch (\Magento\Framework\Exception\LocalizedException $e) {
            $result = ['error' => true, 'message' => $e->getMessage()];
            $subject->getResponse()->representJson(
                $this->objectManager->get('Magento\Framework\Json\Helper\Data')->jsonEncode($result)
            );
        }
    }
}

Step 3: Create SR/RewriteWidget/Helper/Data.php


namespace SR\RewriteWidget\Helper;

class Data extends \Magento\Framework\App\Helper\AbstractHelper
{
    protected $_reservedData = array(
        'type', 'name_in_layout', 'area', 'module_name',
        'name', '_is_changed', '_renderer_name'
    );

    public function decodeWidgetValues($values)
    {
        if (!is_array($values)) {
            return base64_decode(strtr($values, ':_-', '+/='));
        }

        foreach ($values as $key => $value) {
            if ( is_scalar($value) && !in_array($key, $this->_reservedData) ) {
                $values[$key] = base64_decode(strtr($values, ':_-', '+/='));
            }
        }

        return $values;
    }

    public function encodeWidgetValues($values)
    {
        if ( !is_array($values) && !in_array($values, $this->_reservedData) ) {
            return strtr(base64_encode($values), '+/=', ':_-');
        }

        foreach ($values as $key => $value) {
            if (is_scalar($value)) {
                $values[$key] = strtr(base64_encode($value), '+/=', ':_-');
            }
        }

        return $values;
    }
}

And finally, in my widget block, I had to decode all data on-the-fly:


# SR\RewriteWidget\Block\Widget\Content

namespace SR\RewriteWidget\Block\Widget;

class Content extends \Magento\Framework\View\Element\Template implements \Magento\Widget\Block\BlockInterface
{
	/**
	 * @var \SR\RewriteWidget\Helper\Data
	 */
	protected $helper;

	/**
	 * @var \Magento\Cms\Model\Template\FilterProvider
	 */
	protected $filterProvider;

	/**
	 * Constructor
	 *
	 * @param \Magento\Framework\View\Element\Template\Context $context
	 * @param \SR\RewriteWidget\Helper\Data $helper
	 * @param array $data
	 */
	public function __construct(
		\Magento\Framework\View\Element\Template\Context $context,
		\SR\RewriteWidget\Helper\Data $helper,
		\Magento\Cms\Model\Template\FilterProvider $filterProvider,
		array $data = []
	) {
		parent::__construct($context, $data);
		$this->helper = $helper;
		$this->filterProvider = $filterProvider;
	}

	public function getData($key = '', $index = null)
	{
		if ('' === $key) {
			$data = $this->helper->decodeWidgetValues($this->_data);
		} else {
			$data = parent::getData($key, $index);
			if (is_scalar($data)) {
				$data = $this->helper->decodeWidgetValues($data);
			}

			$data = $this->filterProvider->getPageFilter()->filter($data);
		}

		return $data;
	}
}

How to Create a Custom Logger in Magento 2?

Magento 2 logger component is based on Monolog. Magento 2 has three types of logger handler class.

  1. System
  2. Debug
  3. Exception

Today I create my own logger, How to?

Step 1: Create module.xml


<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="SR_CustomLogger" setup_version="2.0.0">
    </module>
</config>

Step 2: Create registration.php


\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'SR_CustomLogger',
    __DIR__
);

Step 3: create 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="SR\CustomLogger\Logger\Handler">
        <arguments>
            <argument name="filesystem" xsi:type="object">Magento\Framework\Filesystem\Driver\File</argument>
        </arguments>
    </type>
    <type name="SR\CustomLogger\Logger\Logger">
        <arguments>
            <argument name="name" xsi:type="string">customLogger
            <argument name="handlers"  xsi:type="array">
                <item name="system" xsi:type="object">SR\CustomLogger\Logger\Handler</item>
            </argument>
        </arguments>
    </type>
</config>


Step 4: Create Handler.php


namespace SR\CustomLogger\Logger;

class Handler extends \Magento\Framework\Logger\Handler\Base
{
    protected $fileName = '/var/log/custom_logger.log';
    protected $loggerType = \Monolog\Logger::INFO;
}

Step 5: Create Logger.php


namespace SR\CustomLogger\Logger;


class Logger extends \Monolog\Logger
{

}

Done!

So now test new logger. Here I am going to inject login event observer.

So create a events.xml [SR/CustomLogger/etc/frontend/events.xml]


<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="customer_customer_authenticated">
        <observer name="custom_logger_customer_authenticated" instance="SR\CustomLogger\Observer\Authenticated" />
    </event>
</config>

Create a Authenticated.php observer for that.



namespace SR\CustomLogger\Observer;

use Magento\Framework\Event\ObserverInterface;

class Authenticated implements ObserverInterface
{
    /**
     * @var \SR\CustomLogger\Logger\Logger $logger
     */
    protected $logger;

    /**
     * @param \SR\CustomLogger\Logger\Logger $logger
     */
    public function __construct(
        \SR\CustomLogger\Logger\Logger $logger
    ) {
        $this->logger = $logger;
    }

    /**
     * Upgrade customer password hash when customer has logged in
     *
     * @param \Magento\Framework\Event\Observer $observer
     * @return void
     */
    public function execute(\Magento\Framework\Event\Observer $observer)
    {
        /** @var \Magento\Customer\Model\Customer $model */
        $model = $observer->getEvent()->getData('model');
        if($model->getId()) {
            $this->logger->info('TEST Custom Logger');
        }
    }
}

Download full module from Here

Magento 2 Order Delivery Date extension

The Order Delivery Date Extension gives your customers possibility to choose the date on which they want the products purchased from your store to be delivered.

Feature:

      1. Simple and quick installation
      2. Allows users to provide expected shipping arrival date
      3. Users can also write comments while placing an order from your online shop
      4. Option to display shipping arrival date and comment in order view page
      5. Previous date selection is not possible
      6. Disable week off days (like Saturday and Sunday) [Stores -> Configuration -> Sales -> Order Delivery Date Settings]
      7. 100% open source
      8. No limitations, no extra costs

Download ‘Order Delivery Date’ extension for Magento 2 from here

Enjoy!

How to create custom product type in magento2

Sometimes you need some extra product type for some needs. So today I discuss about how you can achieve this.

1. Create module.xml [app/code/SR/CustomProductType/etc]


<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="SR_CustomProductType" setup_version="2.0.0">
    </module>
</config>

2. Create registration.php [app/code/SR/CustomProductType]


<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'SR_CustomProductType',
    __DIR__
);


3. Create composer.json [app/code/SR/CustomProductType]


{
    "name": "sr/module-custom-product-type",
    "description": "N/A",
    "require": {
        "php": "~5.5.0|~5.6.0|~7.0.0",
        "magento/module-store": "100.0.0",
        "magento/module-backend": "100.0.0",
        "magento/framework": "100.0.0",
        "lib-libxml": "*"
    },
    "type": "magento2-module",
    "version": "100.0.0",
    "license": [
        "OSL-3.0",
        "AFL-3.0"
    ],
    "autoload": {
        "files": [ "registration.php" ],
        "psr-4": {
            "SR\\CustomProductType\\": ""
        }
    }
}

In Magento2, xml configuration file are divided into small file. So for product type, you need to create product_types.xml inside [app/code/SR/CustomProductType/etc]


<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Catalog:etc/product_types.xsd">
    <type name="customProductType" label="Custom Product Type" modelInstance="SR\CustomProductType\Model\Product\Type\CustomType" indexPriority="100" sortOrder="100" isQty="true">
        <priceModel instance="SR\CustomProductType\Model\Product\Price" />
        <customAttributes>
            <attribute name="refundable" value="true"/>
            <attribute name="taxable" value="true"/>
        </customAttributes>
    </type>
    <composableTypes>
        <type name="customProductType" />
    </composableTypes>
</config>


Here,
name: custom product type code
label: Type view
modelInstance: Model class
priceModel: Price model, you can calculate price your custom need

So now create modelInstance [SR/CustomProductType/Model/Product/Type/CustomType.php]


<?php
namespace SR\CustomProductType\Model\Product\Type;

class CustomType extends \Magento\Catalog\Model\Product\Type\AbstractType
{
    /**
     * Delete data specific for Custom product type
     *
     * @param \Magento\Catalog\Model\Product $product
     * @return void
     */
    public function deleteTypeSpecificData(\Magento\Catalog\Model\Product $product)
    {
    }
}

Create priceModel [SR/CustomProductType/Model/Product/Price.php]


<?php
namespace SR\CustomProductType\Model\Product;

class Price extends \Magento\Catalog\Model\Product\Type\Price
{

}

Now create a setup installer for this module
[SR/CustomProductType/Setup/InstallData.php]


<?php
namespace SR\CustomProductType\Setup;

use Magento\Eav\Setup\EavSetup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;

/**
 * Class InstallData
 *
 * @package 
 */
class InstallData implements InstallDataInterface
{
    /**
     * EAV setup factory
     *
     * @var EavSetupFactory
     */
    private $eavSetupFactory;

    /**
     * Init
     *
     * @param EavSetupFactory $eavSetupFactory
     */
    public function __construct(
        EavSetupFactory $eavSetupFactory
    ) {
        $this->eavSetupFactory = $eavSetupFactory;
    }

    /**
     * @param ModuleDataSetupInterface $setup
     * @param ModuleContextInterface $context
     */
    public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {

        /** @var EavSetup $eavSetup */
        $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);

        $attributes = [
            'cost',
            'price',
            'special_price',
            'special_from_date',
            'special_to_date',
            'weight',
            'tax_class_id'
        ];

        foreach ($attributes as $attributeCode) {
            $relatedProductTypes = explode(
                ',',
                $eavSetup->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeCode, 'apply_to')
            );
            if (!in_array('customProductType', $relatedProductTypes)) {
                $relatedProductTypes[] = 'customProductType';
                $eavSetup->updateAttribute(
                    \Magento\Catalog\Model\Product::ENTITY,
                    $attributeCode,
                    'apply_to',
                    implode(',', $relatedProductTypes)
                );
            }
        }
    }
}


Run following command


php bin/magento setup:upgrade

You can download full module from here.

That’s it!

Process indexing from admin in Magento2

In magento2, you unable to process indexing from admin. You can only set the mode. If you need to run indexing from admin, then this module will help you.

This is independent module, so that this module can’t create any effect to core indexer module. You can use without hesitate.

After install this module go to Admin -> System -> Product Index Management
Download module from here