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

How to add configurable editor in Magento2 admin form

If you miss create admin module series then click here

So I working with following admin form function


SR\Weblog\Block\Adminhtml\Blog\Edit\Tab\Main

So for editor configuration I am going to create a configuration class


SR\Weblog\Block\Adminhtml\Blog\Editor\Editor

namespace SR\Weblog\Block\Adminhtml\Blog\Editor;


class Editor extends \Magento\Framework\Data\Form\Element\Textarea
{
    /**
     * Adminhtml data
     *
     * @var \Magento\Backend\Helper\Data
     */
    protected $_backendData = null;

    /**
     * Module data
     *
     * @var \Magento\Framework\Module\Manager
     */
    protected $_moduleManager = null;

    /**
     * @var \Magento\Cms\Model\Wysiwyg\Config
     */
    protected $_wysiwygConfig;

    /**
     * @var \Magento\Framework\View\LayoutInterface
     */
    protected $_layout;

    /**
     * @param \Magento\Framework\Data\Form\Element\Factory $factoryElement
     * @param \Magento\Framework\Data\Form\Element\CollectionFactory $factoryCollection
     * @param \Magento\Framework\Escaper $escaper
     * @param \Magento\Cms\Model\Wysiwyg\Config $wysiwygConfig
     * @param \Magento\Framework\View\LayoutInterface $layout
     * @param \Magento\Framework\Module\Manager $moduleManager
     * @param \Magento\Backend\Helper\Data $backendData
     * @param array $data
     */
    public function __construct(
        \Magento\Framework\Data\Form\Element\Factory $factoryElement,
        \Magento\Framework\Data\Form\Element\CollectionFactory $factoryCollection,
        \Magento\Framework\Escaper $escaper,
        \Magento\Cms\Model\Wysiwyg\Config $wysiwygConfig,
        \Magento\Framework\View\LayoutInterface $layout,
        \Magento\Framework\Module\Manager $moduleManager,
        \Magento\Backend\Helper\Data $backendData,
        array $data = []
    ) {
        $this->_wysiwygConfig = $wysiwygConfig;
        $this->_layout = $layout;
        $this->_moduleManager = $moduleManager;
        $this->_backendData = $backendData;
        parent::__construct($factoryElement, $factoryCollection, $escaper, $data);
    }

    /**
     * Retrieve additional html and put it at the end of element html
     *
     * @return string
     */
    public function getAfterElementHtml()
    {
        $config = $this->_wysiwygConfig->getConfig();
        $config = json_encode($config->getData());

        $html = parent::getAfterElementHtml();
        if ($this->getIsWysiwygEnabled()) {
            $html = <<<HTML

require([
    'jquery',
    'mage/adminhtml/wysiwyg/tiny_mce/setup'
], function(jQuery){

var config = $config,
    editor;

jQuery.extend(config, {
    settings: {
        theme_advanced_buttons1 : 'bold,italic,|,justifyleft,justifycenter,justifyright,|,' +
            'fontselect,fontsizeselect,|,forecolor,backcolor,|,link,unlink,image,|,bullist,numlist,|,code',
        theme_advanced_buttons2: null,
        theme_advanced_buttons3: null,
        theme_advanced_buttons4: null,
        theme_advanced_statusbar_location: null
    },
    files_browser_window_url: false
});

editor = new tinyMceWysiwygSetup(
    '{$this->getHtmlId()}',
    config
);

editor.turnOn();

jQuery('#{$this->getHtmlId()}')
    .addClass('wysiwyg-editor')
    .data(
        'wysiwygEditor',
        editor
    );
});

HTML;
        }
        return $html;
    }

    /**
     * Check whether wysiwyg enabled or not
     *
     * @return bool
     * @SuppressWarnings(PHPMD.BooleanGetMethodName)
     */
    public function getIsWysiwygEnabled()
    {
        return true;
    }

Now inside


SR\Weblog\Block\Adminhtml\Blog\Edit\Tab\Main

add following line:


$fieldset->addField(
            'content',
            'SR\Weblog\Block\Adminhtml\Blog\Editor\Editor',
            [
                'name' => 'content',
                'required' => true,
            ]
        );

Click here for updated code

How to programmatically create invoice in magento2

If we need to programmatically create invoice for an order then you can create by following way.


// Load the order

 $order = $this->_objectManager->create('Magento\Sales\Model\Order')
    ->loadByAttribute('increment_id', '000000009');

// OR

$order = $this->_objectManager->create('Magento\Sales\Model\Order')
    ->load('1');


if ($order->canInvoice()) {
    // Create invoice for this order
    $invoice = $this->_objectManager->create('Magento\Sales\Model\Service\InvoiceService')->prepareInvoice($order);

    // Make sure there is a qty on the invoice
    if (!$invoice->getTotalQty()) {
        throw new \Magento\Framework\Exception\LocalizedException(
                    __('You can\'t create an invoice without products.')
                );
    }

    // Register as invoice item
    $invoice->setRequestedCaptureCase(\Magento\Sales\Model\Order\Invoice::CAPTURE_OFFLINE);
    $invoice->register();

    // Save the invoice to the order
    $transaction = $this->_objectManager->create('Magento\Framework\DB\Transaction')
        ->addObject($invoice)
        ->addObject($invoice->getOrder());

    $transaction->save();

    // Magento\Sales\Model\Order\Email\Sender\InvoiceSender
    $this->invoiceSender->send($invoice);
    
    $order->addStatusHistoryComment(
        __('Notified customer about invoice #%1.', $invoice->getId())
    )
        ->setIsCustomerNotified(true)
        ->save();
}

How to add custom javascript in magento 2

Magento 2 ship with most popular javascript library like Jquery, requireJS, KnockoutJS. Today I discuss, how you can create a custom javascript and load using requireJS. So lets start…

If you miss my previous post Click Here

Create a custom js file following location.

SR/Js/view/frontend/web/js/srjs.js
define([
    "jquery",
    "jquery/ui"
], function($) {
    "use strict";

    //creating jquery widget
    $.widget('mage.srjs', {
        _create: function() {
            console.log('hey, srjs is loaded!')
            //bind click event of elem id
            this.element.on('click', function(e){
                console.log('Click ME!')
            });
        }

    });

    return $.mage.srjs;
});

Create a phtml file. location

SR/Js/view/frontend/templates/js.phtml
<div>
    <p id="elem">Click Me!</p>
</div>
<script type="text/x-magento-init">
{
    "#elem": { "SR_Js/js/srjs": {} }
}
</script>

That’s it!

Magento 2 Trashcan extension

It might have happened with each of the Magento 2 store owners out there that you deleted any product by mistakes. This extension might come in handy at that time to recovers those deleted products from the trashcan. A must have extension for all Magento 2 stores.

Feature:

      Simple and quick installation
      Restore of products, including metadata and images
      100% open source
      No limitations, no extra costs
      Restore easily from the trashcan (Trashcan > Manage Trashcan)

Download TrashCan for Magento 2 from here

Enjoy!

How to add custom field to Shipping Address form in Magento 2 Onepage Checkout

Magento2 checkout is much improvement than magento 1. It’s complex too. So adding new field is not same as magento 1, but in magento 2 it’s simple. So we go with much explanation for adding custom input field.

Suppose vendor name SR and Module name is CheckoutAdditionalField

Step 1: Create module.xml

app/code/SR/CheckoutAdditionalField/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_CheckoutAdditionalField" setup_version="2.0.0">
        <sequence>
            <module name="Magento_Checkout"/>
        </sequence>
    </module>
</config>

Step 2: Create composer.json

app/code/SR/CheckoutAdditionalField/composer.json
{
    "name": "sr/module-checkoutadditionalfield",
    "description": "Checkout Additional Field",
    "require": {
        "php": "~5.5.0|~5.6.0|~7.0.0",
        "magento/framework": "100.0.*",
        "magento/module-ui": "100.0.*",
        "magento/module-config": "100.0.*",
        "magento/module-checkout": "100.0.*"
    },
    "type": "magento2-module",
    "version": "100.0.0",
    "license": [
        "OSL-3.0",
        "AFL-3.0"
    ],
    "autoload": {
        "files": [ "registration.php" ],
        "psr-4": {
            "SR\\CheckoutAdditionalField\\": ""
        }
    }
}

Step 3: Create registration.php

app/code/SR/CheckoutAdditionalField/registration.php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'SR_CheckoutAdditionalField',
    __DIR__
);

Step 4: Now need to overwrite Magento\Checkout\Block\Checkout\LayoutProcessor process method. So create a plugin,

SR/CheckoutAdditionalField/Plugin/Checkout/Model/Checkout/LayoutProcessor.php
namespace SR\CheckoutAdditionalField\Plugin\Checkout\Model\Checkout;


class LayoutProcessor
{
    /**
     * @param \Magento\Checkout\Block\Checkout\LayoutProcessor $subject
     * @param array $jsLayout
     * @return array
     */
    public function afterProcess(
        \Magento\Checkout\Block\Checkout\LayoutProcessor $subject,
        array  $jsLayout
    ) {
        $jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']
        ['shippingAddress']['children']['shipping-address-fieldset']['children']['delivery_date'] = [
            'component' => 'Magento_Ui/js/form/element/abstract',
            'config' => [
                'customScope' => 'shippingAddress',
                'template' => 'ui/form/field',
                'elementTmpl' => 'ui/form/element/date',
                'options' => [],
                'id' => 'delivery-date'
            ],
            'dataScope' => 'shippingAddress.delivery_date',
            'label' => 'Delivery Date',
            'provider' => 'checkoutProvider',
            'visible' => true,
            'validation' => [],
            'sortOrder' => 250,
            'id' => 'delivery-date'
        ];

        $jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']
        ['shippingAddress']['children']['shipping-address-fieldset']['children']['drop_down'] = [
            'component' => 'Magento_Ui/js/form/element/select',
            'config' => [
                'customScope' => 'shippingAddress',
                'template' => 'ui/form/field',
                'elementTmpl' => 'ui/form/element/select',
                'id' => 'drop-down',
            ],
            'dataScope' => 'shippingAddress.drop_down',
            'label' => 'Drop Down',
            'provider' => 'checkoutProvider',
            'visible' => true,
            'validation' => [],
            'sortOrder' => 251,
            'id' => 'drop-down',
            'options' => [
                [
                    'value' => '',
                    'label' => 'Please Select',
                ],
                [
                    'value' => '1',
                    'label' => 'First Option',
                ]
            ]
        ];

        return $jsLayout;
    }
}

You can download this sample module from here