We sacrifice by not doing any other technology, so that you get the best of Magento.

We sacrifice by not doing any other technology, so that you get the best of Magento.

    How to set layered navigation filters position category wise in Magento 2?

    By default, Magento adjusts the attribute position when we create product attributes. But, systematically, the same position will get apply for all categories.

    So if we wanted to display attributes position differently for categories wise, then this article is your solution.

    First, you have to create text type Category Attributes
    which you need to set differently category wise (Please verify product attribute code and category attribute code).

    In the text type attributes, you have to set the require position value as shown in below screenshot.

    After creating all the category attributes and setting value in all categories, you have to move forword in the next step:

    Override the MagentoCatalogModelLayerCategoryFilterableAttributeList file.

    For that, you need to use virtualType in di.xml in your module (You can create a new module or use any existing module. Create only di.xml in that module).

    <virtualType name="categoryFilterList" type="[Vendor Name][Module Name]ModelLayerFilterList">
        <arguments>
            <argument name="filterableAttributes" xsi:type="object">MagentoCatalogModelLayerCategoryFilterableAttributeList</argument>
        </arguments>
    </virtualType>

    After override FilterList, put below code in that file for change the Filter Position:

    <?php
    namespace [Vendor Name][Module Name]ModelLayer;
    
    /**
     * Override FilterList Class
     */
    class FilterList extends MagentoCatalogModelLayerFilterList {
    
        protected $_categoryFactory;
    
        public function __construct(MagentoCatalogModelCategoryFactory $categoryFactory, MagentoFrameworkViewElementTemplateContext $context, MagentoFrameworkRegistry $registry) {
            $this->_registry = $registry;
            $this->_categoryFactory = $categoryFactory;
            parent::__construct($context);
        }
    
        public function getFilters(MagentoCatalogModelLayer $layer) {
            $arr = array();
            $new = array();
            $value = array();
            if (!count($this->filters)) {
                $this->filters = [$this->objectManager->create($this->filterTypes[self::CATEGORY_FILTER], ['layer' => $layer]), ];
                $i = 1;
                $arraycheck = array();
                $inarraycheck = array();
                $valuesCol = array();
                $allready = array();
                $objectManager = MagentoFrameworkAppObjectManager::getInstance();
                $currentcategory = $this->_registry->registry('current_category');
                $categoryId = $currentcategory->getId();
                $categoryData = $this->_categoryFactory->create()->load($categoryId);
                $getData = $categoryData->getData();
                foreach ($getData as $key => $value) {
                    $new[] = $key;
                }
                foreach ($this->filterableAttributes->getList() as $attribute) {
                    $code = $attribute->getAttributeCode();
                    if (in_array($code, $new)) {
                        if (!empty($getData[$code])) {
                            $valuesCol[] = $posVal = $getData[$code];
                        }
                    }
                }
                foreach ($this->filterableAttributes->getList() as $attribute) {
                    $code = $attribute->getAttributeCode();
                    if (in_array($code, $new)) {
                        if (!empty($getData[$code])) {
                            $posVal = $getData[$code];
                            array_push($inarraycheck, $attribute->getAttributeCode());
                            if (array_key_exists($posVal, $this->filters)) {
                                $arraycheck[] = $this->createAttributeFilter($attribute, $layer);
                                $this->filters[$posVal] = $this->createAttributeFilter($attribute, $layer);
                            } else {
                                $this->filters[$posVal] = $this->createAttributeFilter($attribute, $layer);
                            }
                        } else {
                            $this->filters[$i] = $this->createAttributeFilter($attribute, $layer);
                        }
                    } else {
                        if (in_array($i, $valuesCol)) {
                            $allready[] = $this->createAttributeFilter($attribute, $layer);
                        } else {
                            $this->filters[$i] = $this->createAttributeFilter($attribute, $layer);
                        }
                    }
                    $i++;
                    if (!empty($arraycheck)) {
                        foreach ($arraycheck as $key => $value) {
                            if (!in_array($attribute->getAttributeCode(), $inarraycheck)) {
                                $this->filters[$i] = $value;
                            }
                            $i++;
                            $arraycheck = array();
                        }
                    }
                    if (!empty($allready)) {
                        foreach ($allready as $key1 => $value1) {
                            $this->filters[$i] = $value1;
                        }
                        $i++;
                        $allready = array();
                    }
                }
                ksort($this->filters);
            }
            return $this->filters;
        }
    }
    ?>

    After doing all the steps run below commands and check output on front end.

    You will see layered navigation filters are displayed differently in each category.

    php bin/magento setup:di:compile
    php bin/magento cache:flush

     

    Overriding MagentoCatalogModelLayer Model class in Magento 2

    To customize the layered navigation in Magento 2, sometimes, we need to override the core class file. Generally, we can use preference to override models, but it’s not possible every time.

    So on that case, we need to use a different approach and have to use virtualType to achieve that.

    Please find below the steps to override models using virtualType.

    1. Create an di.xml in : /app/code/Magemonkeys/LayerModel/etc

    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
        <virtualType name="categoryFilterList" type="MagemonkeysLayerModelModelLayerFilterList">
          <arguments>
             <argument name="filterableAttributes" xsi:type="object">MagentoCatalogModelLayerCategoryFilterableAttributeList</argument>
          </arguments>
        </virtualType> 
    </config>

    2. Create an FilterList.php in : /app/code/Magemonkeys/LayerModel/Model/Layer

    <?php
     namespace MagemonkeysLayerModelModelLayer;
     class FilterList extends MagentoCatalogModelLayerFilterList
     {
         public function getFilters(MagentoCatalogModelLayer $layer)
         { 
           if (!count($this->filters)) {
              $this->filters = [
                 $this->objectManager->create($this->filterTypes[self::CATEGORY_FILTER], ['layer' => $layer]),
              ];
              foreach ($this->filterableAttributes->getList() as $attribute) {
                 $this->filters[] = $this->createAttributeFilter($attribute, $layer);
              }
          }
        return $this->filters;
       }
     }
    ?>

    Magento 2.3.x Attributes options not created correctly programmatically

    If you want to create product attribute options programatically, but not by admin then this blog post is right the solution for you.

    You need to add below code in controller.

    Codes for 2.2.x & 2.3.x will be different.

    Code for Magento 2.2.X :

    $optionLabel = $this->optionLabelFactory->create();
    $optionLabel->setStoreId(0);
    $optionLabel->setLabel($attoption);
    
    $option = $this->optionFactory->create();
    $option->setLabel($optionLabel);
    $option->setStoreLabels([$optionLabel]);
    $option->setSortOrder(0);
    $option->setIsDefault(false);
    
    $this->attributeOptionManagement->add(MagentoCatalogModelProduct::ENTITY,
    $this->attributeRepository->get($attrname)->getAttributeId(),$option);
    
    $_product = $this->productFactory->create();
    $isAttributeExist = $_product->getResource()->getAttribute($attrname);
    $optionId = '';
    if ($isAttributeExist && $isAttributeExist->usesSource()) {
        $optionId = $isAttributeExist->getSource()->getOptionId($attoption);
    }
    
    return $optionId;

    Code for Magento 2.3.X

    $optionLabel = $this->optionLabelFactory->create();
    $optionLabel->setStoreId(0);
    $optionLabel->setLabel($attoption);
    
    $option = $this->optionFactory->create();
    $option->setLabel($attoption);
    $option->setStoreLabels([$optionLabel]);
    $option->setSortOrder(0);
    $option->setIsDefault(false);
    
    $this->attributeOptionManagement->add(MagentoCatalogModelProduct::ENTITY,
    $this->attributeRepository->get($attrname)->getAttributeId(),$option);
    
    $_product = $this->productFactory->create();
    $isAttributeExist = $_product->getResource()->getAttribute($attrname);
    $optionId = '';
    if ($isAttributeExist && $isAttributeExist->usesSource()) {
        $optionId = $isAttributeExist->getSource()->getOptionId($attoption);
    }
    
    return $optionId;

    I hope adding the above codes in their respective versions of your Magento store will help you to create product attribute options. Let us know by comment if it worked for you or not?

    Get layered navigation attributes list Magento 2

    Get all the attributes which are used in layered navigation in category & search page.

    You can get all the filterable attribute list from MagentoCatalogModelResourceModelProductAttributeCollectionFactory class.

    As a result, you will get that,

    all those attributes which have been used in Layered Navigation status are Filterable (with results) or Filterable (no results).

    So, you need to create __construct( ) function for define dependency of Class.

    <?php
        private $checkoutSession;
    
        public function __construct(
            MagentoCatalogModelResourceModelProductAttributeCollectionFactory $productAttributeCollectionFactory
        ) {
            $this->productAttributeCollectionFactory = $productAttributeCollectionFactory;
        }
    
        public function getFilterableAttributes()
        {
            /** @var MagentoCatalogModelResourceModelProductAttributeCollection $productAttributes */
            $productAttributes = $this->productAttributeCollectionFactory->create();
            $productAttributes->addFieldToFilter(
                ['is_filterable', 'is_filterable_in_search'],
                [[1, 2], 1]
            );
    
            return $productAttributes;
        }

    Now call a function in template file by iterating over a loop to fetch each attribute code,

    $getAttributes = $block->getFilterableAttributes();
    
        foreach ($getAttributes as $attribute) {
            echo $attribute->getAttributeCode();echo "<br>";
        }

    You can get all the filterable attribute used for your store.

    The Result

        price
        manufacturer
        color
        activity
        style_bags
        material
        strap_bags
        features_bags
        gender
        category_gear
        size
        eco_collection
        performance_fabric
        erin_recommends
        new
        sale
        format
        style_bottom
        style_general
        sleeve
        collar
        pattern
        climate

     

    The XML invalid Element indexer, attribute class: [facet ‘pattern’] The value in developer mode

    Please Follow Below steps to resolved such error of Digit Pattern :

    Making below changes on given files will help you to solve the error.

    File Path is : /vendor/magento/framework/Indexer/etc/indexer.xsd

    Do below change in line number 74:

    <xs:pattern value="[a-zA-Z\]+" /> to <xs:pattern value="[a-zA-Z0-9\]+" />

    File Path is : /vendor/magento/framework/Indexer/etc/indexer_merged.xsd

    Do below change in line number 64:

    <xs:pattern value="[a-zA-Z\]+" /> to <xs:pattern value="[a-zA-Z0-9\]+" />

    File Path is : /vendor/magento/framework/Mview/etc/mview.xsd

    Do below change in line number Line58:

    <xs:pattern value="[a-zA-Z\]+" /> to <xs:pattern value="[a-zA-Z0-9\]+" />

    Clean the cache and rerun the program.

    Let us know via if this tutorial helps you to overcome the problem or not?

    How to add some logic when product added from backend in Magento?

    We will achieve this functionality through EventObserver.

    Here, I have used the event which is called

    “controller_action_catalog_product_save_entity_after”

    Step 1: Create event.xml in /etc/adminhtml folder in your module.

    <?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="controller_action_catalog_product_save_entity_after">
            <observer name="magemonkeys_saveproductlogic" instance="MagemonkeysSaveproductlogicObserverSaveProduct" />
        </event>
    </config>
    

    Step 2: Create a SaveProduct.php Observer in Observer Folder.

    <?php
    namespace MagemonkeysSaveproductlogicObserver;
    
    use MagentoFrameworkEventObserverInterface;
    
    class SaveProduct implements ObserverInterface
    {
        protected $catalogData;
    
        protected $_resource;
    
        
        public function __construct(
            MagentoFrameworkAppResourceConnection $resource
            )
        {
            $this->_resource = $resource;
        }
    
        
        public function execute(MagentoFrameworkEventObserver $observer)
        {
            $connection = $this->_resource->getConnection();
            $table_name = $this->_resource->getTableName('product_oem');
            $productController = $observer->getController();
            $data = $productController->getRequest()->getPostValue();
    
            $mainProduct = $observer->getProduct();
            $productId = $mainProduct->getId();
            /* You can use you logic here */
    
            if(isset($data['product']['product_oem']) && $productId){
                $connection->query('DELETE FROM ' . $table_name . ' WHERE product_id =  ' . (int)$productId . ' ');
                $productOems = $data['product']['product_oem'];
                if(!is_array($productOems)){
                    $productOems = array();
                    $productOems[] = (int)$data['product']['product_oem'];
                }
                foreach ($productOems as $k => $v) {
                    $connection->query('INSERT INTO ' . $table_name . ' VALUES ( ' . $v . ', ' . (int)$productId . ',0)');
                }
            }
        }
    }
    ?>

    After creating the above module, please run the upgrade, compile and static:content:deploy command to implement this module.

    Magento 2: Exclude group products in category page using plugin

    Magento 2: Exclude group products in list page using a plugin

    If you want to exclude group products in category page then follow below steps:

    1. Put below code in your module di.xml file.

    <type name="MagentoCatalogModelLayer">
     <plugin name="LayerPlugin" type="VendernameModulenamePluginLayer"/>
    </type>

    2. The next step is to create Layer.php file in your module and paste below code in VendernameModulenamePlugin folder

    <?php
    namespace VendernameModulenamePlugin;
    
    use MagentoCatalogApiCategoryRepositoryInterface;
    use MagentoFrameworkExceptionNoSuchEntityException;
    use MagentoCatalogModelResourceModelProductAttributeCollectionFactory as AttributeCollectionFactory;
    
    class Layer
    {
     /**
     * Product collections array
     *
     * @var array
     */
     protected $_productCollections = [];
    
     /**
     * Key which can be used for load/save aggregation data
     *
     * @var string
     */
     protected $_stateKey = null;
    
     /**
     * Core registry
     *
     * @var MagentoFrameworkRegistry
     */
     protected $registry = null;
    
     /**
     * Store manager
     *
     * @var MagentoStoreModelStoreManagerInterface
     */
     protected $_storeManager;
    
     /**
     * Catalog product
     *
     * @var MagentoCatalogModelResourceModelProduct
     */
     protected $_catalogProduct;
    
     /**
     * Attribute collection factory
     *
     * @var AttributeCollectionFactory
     */
     protected $_attributeCollectionFactory;
    
     /**
     * Layer state factory
     *
     * @var MagentoCatalogModelLayerStateFactory
     */
     protected $_layerStateFactory;
    
     /**
     * @var MagentoCatalogModelLayerItemCollectionProviderInterface
     */
     protected $collectionProvider;
    
     /**
     * @var MagentoCatalogModelLayerCategoryStateKey
     */
     protected $stateKeyGenerator;
    
     /**
     * @var MagentoCatalogModelLayerCategoryCollectionFilter
     */
     protected $collectionFilter;
    
     /**
     * @var CategoryRepositoryInterface
     */
     protected $categoryRepository;
    
     /**
     * @param LayerContextInterface $context
     * @param LayerStateFactory $layerStateFactory
     * @param AttributeCollectionFactory $attributeCollectionFactory
     * @param MagentoCatalogModelResourceModelProduct $catalogProduct
     * @param MagentoStoreModelStoreManagerInterface $storeManager
     * @param MagentoFrameworkRegistry $registry
     * @param CategoryRepositoryInterface $categoryRepository
     * @param array $data
     */
     public function __construct(
     MagentoCatalogModelLayerStateFactory $layerStateFactory,
     AttributeCollectionFactory $attributeCollectionFactory,
     MagentoCatalogModelResourceModelProduct $catalogProduct,
     MagentoStoreModelStoreManagerInterface $storeManager,
     MagentoFrameworkRegistry $registry,
     CategoryRepositoryInterface $categoryRepository,
     array $data = []
     ) {
     $this->_layerStateFactory = $layerStateFactory;
     $this->_attributeCollectionFactory = $attributeCollectionFactory;
     $this->_catalogProduct = $catalogProduct;
     $this->_storeManager = $storeManager;
     $this->registry = $registry;
     $this->categoryRepository = $categoryRepository; 
    
     }
    
     public function afterGetProductCollection(MagentoCatalogModelLayer $subject, $collection)
     {
     $collection->addAttributeToFilter('type_id', array('eq' => 'grouped'));
     return $collection;
     }
     
    }

    Magento 2 Sorting Attribute Option value in category filter programmatically

    Step 1) Override the Magento_LayeredNavigation/templates/layer/filter.phtml to your theme design directory

    Step 2) Add and Change in filter.phtml

    <?php 
        $offsets = array();
        $offsets1 = array();
        $offsets2 = array();
        $offsets3 = array();
        foreach ($filterItems as $filters) {
            $checklable = $filters->getLabel();
            if(preg_match("/[a-z]/i", $checklable)){
                array_push($offsets1, $filters);
            }
            elseif(preg_match("/,/i", $checklable)){
                array_push($offsets2, $filters);
            }
            elseif(preg_match("/-/i", $checklable)){
                array_push($offsets2, $filters);
            }
            elseif(strstr($checklable, '/')){
                array_push($offsets2, $filters);
            }
            else{
                array_push($offsets3, $filters);
            }
        }
        asort($offsets1);
        asort($offsets2);
        sort($offsets3);
    
        $offsets = array_merge($offsets3,$offsets2,$offsets1);
    ?>
    <ol class="items">
        <?php foreach ($offsets as $filterItem): ?>
            <li class="item">
                <?php //echo '===>'; ?>
                <?php if ($filterItem->getCount() > 0): ?>
                    <a href="<?= $block->escapeUrl($filterItem->getUrl()) ?>">
                        <?= /* @escapeNotVerified */ $filterItem->getLabel() ?>
                        <?php if ($this->helper('MagentoCatalogHelperData')->shouldDisplayProductCountOnLayer()): ?>
                            <span class="count"><?= /* @escapeNotVerified */ $filterItem->getCount() ?><span class="filter-count-label">
                                <?php if ($filterItem->getCount() == 1):?> <?= /* @escapeNotVerified */ __('item') ?><?php else:?> <?= /* @escapeNotVerified */ __('items') ?><?php endif;?></span></span>
                        <?php endif; ?>
                    </a>
                <?php else:?>
                    <?= /* @escapeNotVerified */ $filterItem->getLabel() ?>
                    <?php if ($this->helper('MagentoCatalogHelperData')->shouldDisplayProductCountOnLayer()): ?>
                        <span class="count"><?= /* @escapeNotVerified */ $filterItem->getCount() ?><span class="filter-count-label">
                            <?php if ($filterItem->getCount() == 1):?><?= /* @escapeNotVerified */ __('item') ?><?php else:?><?= /* @escapeNotVerified */ __('items') ?><?php endif;?></span></span>
                    <?php endif; ?>
                <?php endif; ?>
            </li>
        <?php endforeach ?>
    </ol>
    

     

    Steps to disable payment method by specific customer group in Magento 2

    Successful e-commerce store demands to provide better user experience to grow their sales by offering and managing various payment methods at checkout page according to the needs of the specific customer as every customer has different demands. For example, the merchant which offers the cash on delivery for the local region may not be offered to international customers; In some cases, payment gateway requires certain taxes and fees for the different part which cannot be borne by the merchant.TO solve those issues, it has become vital for Magento store owners to optimize their payment methods according to different customer groups

    To enable the payment process between customers and Magento store, we can restrict the payment methods by different customers groups by following the steps below.

    1. Create an events.xml in : /app/code/Magemonkeys/PaymentMethod/etc

    <?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="payment_method_is_active">
     <observer name="Magemonkeys_PaymentMethod_DisabledPgByCustomergroup" instance="MagemonkeysPaymentMethodObserverDisabledPgByCustomergroup" />
     </event>
    </config>

    2. Create an DisabledPgByCustomergroup.php in : /app/code/Magemonkeys/PaymentMethod/Observer

    <?php
    namespace MagemonkeysPaymentMethodObserver;
    use MagentoFrameworkEventObserverInterface;
    use MagentoFrameworkAppRequestDataPersistorInterface;
    use MagentoFrameworkAppObjectManager;
    
    class DisabledPgByCustomergroup implements ObserverInterface
    {
     
     public function execute(MagentoFrameworkEventObserver $observer)
     { 
        $helper = MagentoFrameworkAppObjectManager::getInstance()->get('MagemonkeysPaymentMethodHelperData'); 
        if($helper->isEnable()){
          $customer_grp = $helper->isActiveGroup(); 
          $result = $observer->getEvent()->getResult();
          $method_instance = $observer->getEvent()->getMethodInstance();
          $quote = $observer->getEvent()->getQuote(); 
           if (null !== $quote && $quote->getCustomerGroupId() != 'CUSTOMER_GROUP_ID') { 
            if ($method_instance->getCode() == 'PAYMENT_METHOD_CODE') {
              $result->setData('is_available', false);
            } 
          }
        }
      }
    } 
    ?>

    By following the above steps, you can restrict the payment methods in Magento 2 store.
    If you have any doubt regarding the above method, feel free to ask me the comment section.

    I would be happy to help you.

    Magento 2: Add VAT and Company Number in footer of invoice and shipment PDF

    In this article, I will guide you on, how to add VAT and Company Number in invoice and shipment PDF in Magento 2?

    Follow the below steps:

    You need to override two models in custom extension.

    I assume you know about how to override model.

    Step 1

    Create di.xml file in your module’s etc folder and put below code in it

    <?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="MagentoSalesModelOrderPdfInvoice" type="[vendername][modulename]ModelOrderPdfinvoice" />
     <preference for="MagentoSalesModelOrderPdfShipment" type="[vendername][modulename]OrderPdfshipment" />
    </config>

    Step 2

    Create new model file Pdfinvoice.php in your Model/Order folder and put below code in it.

    <?php
    namespace [vendername][modulename]ModelOrder;
    class Pdfinvoice extends MagentoSalesModelOrderPdfInvoice
    {
     /**
     * Return PDF document
     *
     * @param array|Collection $invoices
     * @return Zend_Pdf
     */
     public function getPdf($invoices = [])
     {
     $this->_beforeGetPdf();
     $this->_initRenderer('invoice');
    
     $pdf = new Zend_Pdf();
     $this->_setPdf($pdf);
     $style = new Zend_Pdf_Style();
     $this->_setFontBold($style, 10);
    
     foreach ($invoices as $invoice) {
     if ($invoice->getStoreId()) {
     $this->_localeResolver->emulate($invoice->getStoreId());
     $this->_storeManager->setCurrentStore($invoice->getStoreId());
     }
     $page = $this->newPage();
     $order = $invoice->getOrder();
     /* Add image */
     $this->insertLogo($page, $invoice->getStore());
     /* Add address */
     $this->insertAddress($page, $invoice->getStore());
     /* Add head */
     $this->insertOrder(
     $page,
     $order,
     $this->_scopeConfig->isSetFlag(
     self::XML_PATH_SALES_PDF_INVOICE_PUT_ORDER_ID,
     MagentoStoreModelScopeInterface::SCOPE_STORE,
     $order->getStoreId()
     )
     );
     
     /* Add document text and number */
     $this->insertDocumentNumber($page, __('Invoice # ') . $invoice->getIncrementId());
     /* Add table */
     $this->_drawHeader($page);
     /* Add body */
     foreach ($invoice->getAllItems() as $item) {
     if ($item->getOrderItem()->getParentItem()) {
     continue;
     }
     /* Draw item */
     $this->_drawItem($item, $page, $order);
     $page = end($pdf->pages);
     }
     /* Add totals */
     $this->insertTotals($page, $invoice);
     if ($invoice->getStoreId()) {
     $this->_localeResolver->revert();
     }
     $this->_drawFooter($page);
     }
     $this->_afterGetPdf();
     return $pdf;
     }
    
     protected function _drawFooter(Zend_Pdf_Page $page)
     {
     $this->y = 50; 
     $page->setFillColor(new Zend_Pdf_Color_RGB(1, 1, 1));
     $page->setLineColor(new Zend_Pdf_Color_GrayScale(0.5));
     $page->drawRectangle(25, $this->y, 570, $this->y -30);
     $page->setLineWidth(0.5);
    
     $page->setFillColor(new Zend_Pdf_Color_RGB(0.1, 0.1, 0.1));
     $this->_setFontRegular($page, 10);
     $this->y -=16;
     $page->drawText(__('VAT # xxxxxxxx'), 220, $this->y, 'UTF-8');
     $page->drawText(__('Company # xxxxxxxxx'), 320, $this->y, 'UTF-8');
    
     } 
    }

    Step 3

    Create another new model file Pdfshipment.php in your Model/Order folder and put below code in it.

    <?php
    namespace [vendername][modulename]ModelOrder;
    class Pdfshipment extends MagentoSalesModelOrderPdfShipment
    {
     /**
     * Return PDF document
     *
     * @param MagentoSalesModelOrderShipment[] $shipments
     * @return Zend_Pdf
     */
     public function getPdf($shipments = [])
     {
     $this->_beforeGetPdf();
     $this->_initRenderer('shipment');
    
     $pdf = new Zend_Pdf();
     $this->_setPdf($pdf);
     $style = new Zend_Pdf_Style();
     $this->_setFontBold($style, 10);
     foreach ($shipments as $shipment) {
     if ($shipment->getStoreId()) {
     $this->_localeResolver->emulate($shipment->getStoreId());
     $this->_storeManager->setCurrentStore($shipment->getStoreId());
     }
     $page = $this->newPage();
     $order = $shipment->getOrder();
     /* Add image */
     $this->insertLogo($page, $shipment->getStore());
     /* Add address */
     $this->insertAddress($page, $shipment->getStore());
     /* Add head */
     $this->insertOrder(
     $page,
     $shipment,
     $this->_scopeConfig->isSetFlag(
     self::XML_PATH_SALES_PDF_SHIPMENT_PUT_ORDER_ID,
     MagentoStoreModelScopeInterface::SCOPE_STORE,
     $order->getStoreId()
     )
     );
     /* Add document text and number */
     $this->insertDocumentNumber($page, __('Packing Slip # ') . $shipment->getIncrementId());
     /* Add table */
     $this->_drawHeader($page);
     /* Add body */
     foreach ($shipment->getAllItems() as $item) {
     if ($item->getOrderItem()->getParentItem()) {
     continue;
     }
     /* Draw item */
     $this->_drawItem($item, $page, $order);
     $page = end($pdf->pages);
     }
     if ($shipment->getStoreId()) {
     $this->_localeResolver->revert();
     }
     $this->_drawFooter($page);
     }
     $this->_afterGetPdf();
     return $pdf;
     }
    
     protected function _drawFooter(Zend_Pdf_Page $page)
     {
     $this->y = 50; 
     $page->setFillColor(new Zend_Pdf_Color_RGB(1, 1, 1));
     $page->setLineColor(new Zend_Pdf_Color_GrayScale(0.5));
     $page->drawRectangle(25, $this->y, 570, $this->y -30);
     $page->setLineWidth(0.5);
    
     $page->setFillColor(new Zend_Pdf_Color_RGB(0.1, 0.1, 0.1));
     $this->_setFontRegular($page, 10);
     $this->y -=16;
     $page->drawText(__('VAT # xxxxxxxxx'), 220, $this->y, 'UTF-8');
     $page->drawText(__('Company # xxxxxxxxx'), 320, $this->y, 'UTF-8');
    
     }
    }