3

I want to change Magento2 Offline payment method "Purchase Order" payment Code,with separate module. Do not suggest Make a new Offline payment methods like purchase order.

Current-code: "purchaseorder"

Thanks

  • you mean you want to override the current purchase order and change its payment code? – fmsthird Mar 4 '19 at 10:17
  • 1
    Separate module is recommended and disable the "Purchase Order" seem that better – Tuyen Nguyen Mar 4 '19 at 10:53
6
+50

It would be great if you can create a custom payment method like a Purchase order rather than change payment method code. I have explained all the steps for creating a new offline custom payment method using a custom module same as Purchase Order.

1 Create Registration.php file app/code/{VendorName}/{ModuleName}

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

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

2 Create module.xml file (app/code/{VendorName}/{ModuleName}/etc/module.xml)

<?xml version="1.0"?>
<!--
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Indianic_FortoxFinance" setup_version="1.0.0">
        <sequence>
            <module name="Magento_Store"/>
            <module name="Magento_Catalog"/>
            <module name="Magento_OfflinePayments"/>
            <module name="Magento_Payment"/>
        </sequence>
    </module>
</config>

3 Create payment.xml file in custom module (app/code/{VendorName}/{ModuleName}/etc/payment.xml)

<?xml version="1.0"?>
<!--
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<payment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Payment:etc/payment.xsd">
    <groups>
        <group id="offline">
            <label>Offline Payment Methods</label>
        </group>
    </groups>
    <methods>
        <method name="fortoxfinance">
            <allow_multiple_address>1</allow_multiple_address>
        </method>
    </methods>
</payment>

4 Create config.xml file in custom module (app/code/{VendorName}/{ModuleName}/etc/config.xml)

<?xml version="1.0"?>
<!--
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <payment>
            <fortoxfinance>
                <active>1</active>
                <model>Indianic\FortoxFinance\Model\FortoxFinance</model>
                <order_status>pending</order_status>
                <title>Fortox Finance</title>
                <allowspecific>0</allowspecific>
                <group>offline</group>
            </fortoxfinance>
            <free>
                <group>offline</group>
            </free>
        </payment>
    </default>
</config>

5 Create system.xml file in custom module. (app/code/{VendorName}/{ModuleName}/etc/adminhtml/system.xml)

<?xml version="1.0"?>
<!--
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <section id="payment">
            <group id="fortoxfinance" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>Fortox Finance</label>
                <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1">
                    <label>Enabled</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                </field>
                <field id="order_status" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1">
                    <label>New Order Status</label>
                    <source_model>Magento\Sales\Model\Config\Source\Order\Status\NewStatus</source_model>
                </field>
                <field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0">
                    <label>Sort Order</label>
                    <frontend_class>validate-number</frontend_class>
                </field>
                <field id="title" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Title</label>
                </field>
                <field id="allowspecific" translate="label" type="allowspecific" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1">
                    <label>Payment from Applicable Countries</label>
                    <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model>
                </field>
                <field id="specificcountry" translate="label" type="multiselect" sortOrder="51" showInDefault="1" showInWebsite="1" showInStore="0">
                    <label>Payment from Specific Countries</label>
                    <source_model>Magento\Directory\Model\Config\Source\Country</source_model>
                    <can_be_empty>1</can_be_empty>
                </field>
                <field id="min_order_total" translate="label" type="text" sortOrder="98" showInDefault="1" showInWebsite="1" showInStore="0">
                    <label>Minimum Order Total</label>
                </field>
                <field id="max_order_total" translate="label" type="text" sortOrder="99" showInDefault="1" showInWebsite="1" showInStore="0">
                    <label>Maximum Order Total</label>
                </field>
                <field id="model"></field>
            </group>
        </section>
    </system>
</config>

6 Create Model file in the custom module. (app/code/{VendorName}/{ModuleName}/Model/FortoxFinance.php)

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Indianic\FortoxFinance\Model;
/**
 * Cash on delivery payment method model
 *
 * @method \Magento\Quote\Api\Data\PaymentMethodExtensionInterface getExtensionAttributes()
 *
 * @api
 * @since 100.0.2
 */
class FortoxFinance extends \Magento\Payment\Model\Method\AbstractMethod
{
    /*
     * Payment method code
     *
     * @var string
     */
    protected $_code = 'fortoxfinance';

    /**
     * @var string
     */
    protected $_formBlockType = \Indianic\FortoxFinance\Block\Form\FortoxFinance::class;

    /**
     * @var string
     */
    protected $_infoBlockType = \Indianic\FortoxFinance\Block\Info\FortoxFinance::class;

    /**
     * Assign data to info model instance
     *
     * @param \Magento\Framework\DataObject|mixed $data
     * @return $this
     * @throws LocalizedException
     */
    public function assignData(\Magento\Framework\DataObject $data)
    {
        $this->getInfoInstance()->setPoNumber($data->getPoNumber());
        return $this;
    }

    /**
     * Validate payment method information object
     *
     * @return $this
     * @throws LocalizedException
     * @api
     */
    public function validate()
    {
        parent::validate();

        if (empty($this->getInfoInstance()->getPoNumber())) {
            throw new LocalizedException(__('Purchase order number is a required field.'));
        }

        return $this;
    }
}

7 Create checkout_index_index file in (app/code/{VendorName}/{ModuleName}/view/frontend/layout/checkout_index_index.xml)

<?xml version="1.0"?>
<!--
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="checkout.root">
            <arguments>
                <argument name="jsLayout" xsi:type="array">
                    <item name="components" xsi:type="array">
                        <item name="checkout" xsi:type="array">
                            <item name="children" xsi:type="array">
                                <item name="steps" xsi:type="array">
                                    <item name="children" xsi:type="array">
                                        <item name="billing-step" xsi:type="array">
                                            <item name="children" xsi:type="array">
                                                <item name="payment" xsi:type="array">
                                                    <item name="children" xsi:type="array">
                                                        <item name="renders" xsi:type="array">
                                                            <!-- merge payment method renders here -->
                                                            <item name="children" xsi:type="array">
                                                                <item name="fortox-payments" xsi:type="array">
                                                                    <item name="component" xsi:type="string">Indianic_FortoxFinance/js/view/payment/fortox-payments</item>
                                                                    <item name="methods" xsi:type="array">
                                                                        <item name="fortoxfinance" xsi:type="array">
                                                                            <item name="isBillingAddressRequired" xsi:type="boolean">true</item>
                                                                        </item>
                                                                    </item>
                                                                </item>
                                                            </item>
                                                        </item>
                                                    </item>
                                                </item>
                                            </item>
                                        </item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

8 Create js file fortox-payments.js file in (app/code/{VendorName}/{ModuleName}/view/frontend/web/js/view/payment/fortox-payments.js)

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/* @api */
define([
    'uiComponent',
    'Magento_Checkout/js/model/payment/renderer-list'
], function (Component, rendererList) {
    'use strict';

    rendererList.push(
        {
            type: 'fortoxfinance',
            component: 'Indianic_FortoxFinance/js/view/payment/method-renderer/fortoxfinance-method'
        }
    );

    /** Add view logic here if needed */
    return Component.extend({});
});

9 Create js file fortoxfinance-method.js file in (app/code/{VendorName}/{ModuleName}/view/frontend/web/js/view/payment/method-renderer/fortoxfinance-method.js)

/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

/* @api */
define([
    'Magento_Checkout/js/view/payment/default',
    'jquery',
    'mage/validation'
], function (Component, $) {
    'use strict';

    return Component.extend({
        defaults: {
            template: 'Indianic_FortoxFinance/payment/fortoxfinance-form',
            purchaseOrderNumber: ''
        },

        /** @inheritdoc */
        initObservable: function () {
            this._super()
                .observe('purchaseOrderNumber');
            return this;
        },

        /**
         * @return {Object}
         */
        getData: function () {
            return {
                method: this.item.method,
                'po_number': this.purchaseOrderNumber(),
                'additional_data': null
            };
        },
        /**
         * @return {jQuery}
         */
        validate: function () {
            var form = 'form[data-role=fortoxfinance-form]';
            return $(form).validation() && $(form).validation('isValid');
        }
    });
});

10 Create HTML file fortoxfinance-form.html in (app/code/{VendorName}/{ModuleName}/view/frontend/web/template/payment/fortoxfinance-form.html)

<!--
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<div class="payment-method" data-bind="css: {'_active': (getCode() == isChecked())}">
    <div class="payment-method-title field choice">
        <input type="radio"
               name="payment[method]"
               class="radio"
               data-bind="attr: {'id': getCode()}, value: getCode(), checked: isChecked, click: selectPaymentMethod, visible: isRadioButtonVisible()"/>
        <label data-bind="attr: {'for': getCode()}" class="label">
            <span data-bind="text: getTitle()"></span>
        </label>
    </div>

    <div class="payment-method-content">
        <!-- ko foreach: getRegion('messages') -->
        <!-- ko template: getTemplate() --><!-- /ko -->
        <!--/ko-->
        <div class="payment-method-billing-address">
            <!-- ko foreach: $parent.getRegion(getBillingAddressFormName()) -->
            <!-- ko template: getTemplate() --><!-- /ko -->
            <!--/ko-->
        </div>
        <form id="fortoxfinance-form" class="form form-purchase-order" data-role="fortoxfinance-form">
            <fieldset class="fieldset payment method" data-bind='attr: {id: "payment_form_" + getCode()}'>
                <div class="field field-number required">
                    <label for="po_number" class="label">
                        <span><!-- ko i18n: 'Purchase Order Number'--><!-- /ko --></span>
                    </label>
                    <div class="control">
                        <input type="text"
                               id="po_number"
                               name="payment[po_number]"
                               data-validate="{required:true}"
                               data-bind='
                                attr: {title: $t("Purchase Order Number")},
                                value: purchaseOrderNumber'
                               class="input-text"/>
                    </div>
                </div>
            </fieldset>
        </form>
        <div class="checkout-agreements-block">
            <!-- ko foreach: $parent.getRegion('before-place-order') -->
                <!-- ko template: getTemplate() --><!-- /ko -->
            <!--/ko-->
        </div>
        <div class="actions-toolbar" id="review-buttons-container">
            <div class="primary">
                <button class="action primary checkout"
                        type="submit"
                        data-bind="
                        click: placeOrder,
                        attr: {title: $t('Place Order')},
                        enable: (getCode() == isChecked()),
                        css: {disabled: !isPlaceOrderActionAllowed()}
                        "
                        data-role="review-save">
                    <span data-bind="i18n: 'Place Order'"></span>
                </button>
            </div>
        </div>
    </div>
</div>

11 Create block file i.e FortoxFinance.php (app/code/{VendorName}/{ModuleName}/Block/Form/FortoxFinance.php)

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Indianic\FortoxFinance\Block\Form;

class FortoxFinance extends \Magento\Payment\Block\Form
{
    /**
     * Purchase order template
     *
     * @var string
     */
    protected $_template = 'Indianic_FortoxFinance::form/fortoxfinance.phtml';
}

12 Create another block file i.e FortoxFinance.php (app/code/{VendorName}/{ModuleName}/Block/Info/FortoxFinance.php)

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Indianic\FortoxFinance\Block\Info;

class FortoxFinance extends \Magento\Payment\Block\Info
{
    /**
     * @var string
     */
    protected $_template = 'Indianic_FortoxFinance::info/fortoxfinance.phtml';

    /**
     * @return string
     */
    public function toPdf()
    {
        $this->setTemplate('Indianic_FortoxFinance::info/pdf/fortoxfinance.phtml');
        return $this->toHtml();
    }
}

13 Create template file fortoxfinance.phtml (app/code/{VendorName}/{ModuleName}/view/frontend/templates/form/fortoxfinance.phtml)

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

// @codingStandardsIgnoreFile
/**
 * @var $block \Magento\OfflinePayments\Block\Form\Purchaseorder
 */
$methodCode = $block->escapeHtml($block->getMethodCode());
?>
<fieldset class="fieldset items <?= /* @noEscape */ $methodCode ?>" id="payment_form_<?= /* @noEscape */ $methodCode ?>" style="display: none">
    <div class="field number required">
        <label for="po_number" class="label"><span><?= $block->escapeHtml(__('Purchase Order Number')) ?></span></label>
        <div class="control">
            <input type="text" id="po_number" name="payment[po_number]" title="<?= $block->escapeHtml(__('Purchase Order Number')) ?>"
                   class="input-text required-entry"
                   value="<?= $block->escapeHtml($block->getInfoData('po_number')) ?>" />
        </div>
    </div>
</fieldset>

14 Create another template file fortoxfinance.phtml (app/code/{VendorName}/{ModuleName}/view/frontend/templates/info/fortoxfinance.phtml)

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
/**
 * @var $block \Magento\OfflinePayments\Block\Info\Purchaseorder
 */
?>
<dl class="payment-method purchase order">
    <dt class="title"><?= $block->escapeHtml($block->getMethod()->getTitle()) ?></dt>
    <dd class="content">
        <strong><?= $block->escapeHtml(__('Purchase Order Number')) ?></strong>
        <span class="number"><?= $block->escapeHtml($block->getInfo()->getPoNumber()) ?></span>
    </dd>
</dl>

15 Create template file fortoxfinance.phtml (app/code/{VendorName}/{ModuleName}/view/adminhtml/templates/info/pdf/fortoxfinance.phtml)

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
/**
 * @var $block \Magento\OfflinePayments\Block\Info\Purchaseorder
 */
?>
<?= $block->escapeHtml(__('Purchase Order Number: %1', $block->getInfo()->getPoNumber())) ?>
    {{pdf_row_separator}}

16 Create template file fortoxfinance.phtml (app/code/{VendorName}/{ModuleName}/view/adminhtml/templates/info/fortoxfinance.phtml)

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
/**
 * @var $block \Magento\OfflinePayments\Block\Info\Purchaseorder
 */
?>
<div class="order-payment-method-name"><?= $block->escapeHtml($block->getMethod()->getTitle()) ?></div>
<table class="data-table admin__table-secondary">
    <tr>
        <th><?= $block->escapeHtml(__('Purchase Order Number')) ?>:</th>
        <td><?= $block->escapeHtml($block->getInfo()->getPoNumber()) ?></td>
    </tr>
</table>

17 Create template file fortoxfinance.phtml (app/code/{VendorName}/{ModuleName}/view/adminhtml/templates/form/fortoxfinance.phtml)

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

// @codingStandardsIgnoreFile
/**
 * @var $block \Magento\OfflinePayments\Block\Form\Purchaseorder
 */
?>
<fieldset class="admin__fieldset payment-method" id="payment_form_<?= $block->escapeHtml($block->getMethodCode()) ?>" style="display: none">
    <div class="admin__field _required">
        <label for="po_number" class="admin__field-label"><span><?= $block->escapeHtml(__('Purchase Order Number')) ?></span></label>
        <div class="admin__field-control">
            <input type="text" id="po_number" name="payment[po_number]"
                   title="<?= $block->escapeHtml(__("Purchase Order Number")) ?>" class="required-entry admin__control-text"
                   value="<?= /* @noEscape */ $block->getInfoData('po_number') ?>"/>
        </div>
    </div>
</fieldset>
  • This is such a beautifully crafted answer, Thank you for that! – user2965205 Jul 4 '19 at 0:23
3

The carrier codes are defined in the core files of Magento.

I do not recommend you adjusting the core files but i will tell you how to change them.

If you upgrade your Magento all adjustments will be reverted. You can apply these changes with a module.

 Magento\OfflinePayments\Model\Purchaseorder

change

const PAYMENT_METHOD_PURCHASEORDER_CODE = 'purchaseorder';

and module-offline-payments/etc/config.xml

change

   <purchaseorder>
        <active>0</active>
        <model>Magento\OfflinePayments\Model\Purchaseorder</model>
        <order_status>pending</order_status>
        <title>Purchase Order</title>
        <allowspecific>0</allowspecific>
        <group>offline</group>
    </purchaseorder>

to

        <yourname>
            <active>0</active>
            <model>Magento\OfflinePayments\Model\Purchaseorder</model>
            <order_status>pending</order_status>
            <title>Purchase Order</title>
            <allowspecific>0</allowspecific>
            <group>offline</group>
        </yourname>

Also module-offline-payments\etc\payment.xml

change

 <method name="purchaseorder">
        <allow_multiple_address>1</allow_multiple_address>
    </method>

to

<method name="yourname">
    <allow_multiple_address>1</allow_multiple_address>
</method>

then you need to adjust the group in module-offline-payments\etc\config.xml

<group id="purchaseorder" translate="label" type="text" sortOrder="32" showInDefault="1" showInWebsite="1" showInStore="1">

to

<group id="yourname" translate="label" type="text" sortOrder="32" showInDefault="1" showInWebsite="1" showInStore="1">

After that you need to edit everything that providers the frontend JS

module-offline-payments/view/frontend/layout/checkout_index_index.xml

change

<item name="yourname" xsi:type="array"> 
<item name="isBillingAddressRequired" xsi:type="boolean">true</item>
</item>

and last module-offline-payments/view/frontend/web/js/view/payment/offline-payments.js

change

{
    type: 'yourname',
    component: 'Magento_OfflinePayments/js/view/payment/method-renderer/purchaseorder-method'
}

You can see that this includes changing a whole lot of things. Not even taking in account the already saved data that you need so change in the database. With plugins you might be able to intercept the getPaymentMethod() from the Order/Invoice/Shipment/Credit memo and change it from there but without the explanation why you would change it i do not know your needs.

It would be a lot less risky and more stable to just write a new payment method. It is not that hard to do and you can extend existing Magento classes for it.

So i highly recommend creating a new payment method instead of adjusting the existing ones.

Link to documentation about new payment method here

And then you would just disable purchase order and enable this one. You could even let it extend the purchase order class.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.