1

I'm trying to add a custom order attribute (BC_Pdfinvoice) and make it available in the Order API response.

I created a module and added an extension_attributes.xml file here Vendor/Module/etc/extension_attributes.xml:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
<extension_attributes for="Magento\Sales\Api\Data\OrderInterface">
    <attribute code="BC_Pdfinvoice" type="string"/>
</extension_attributes>
</config>

Then added Vendor/Module/etc/di.xml:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<virtualType name="Magento\Sales\Model\ResourceModel\Order\Grid" type="Magento\Sales\Model\ResourceModel\Grid">
<arguments>
<argument name="columns" xsi:type="array">
<item name="BC_Pdfinvoice" xsi:type="string">sales_order.BC_Pdfinvoice</item>
</argument>
</arguments>
</virtualType>
<type name="Magento\Sales\Api\OrderRepositoryInterface">
<plugin name="BC_Pdfinvoice" type="Vendor\Module\Plugin\Model\Order\AddCustomOrderAttribute" />
</type>
</config>

And the plugin Vendor/Module/Plugin/Model/Order/AddCustomOrderAttribute.php:

<?php

namespace Vendor\Module\Plugin\Model\Order;

use Magento\Sales\Api\Data\OrderSearchResultInterface;
use Magento\Sales\Model\OrderFactory;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Api\Data\OrderExtensionFactory;
use Magento\Sales\Api\Data\OrderInterface;

class AddCustomOrderAttribute
{
    /**
     * @var OrderFactory
     */
    private $orderFactory;

    /**
     * @var OrderExtensionFactory
     */
    private $orderExtensionFactory;

    /**
     * @param OrderExtensionFactory $extensionFactory
     * @param OrderFactory $orderFactory
     */
    public function __construct(
        OrderExtensionFactory $extensionFactory,
        OrderFactory $orderFactory
    ) {
        $this->orderExtensionFactory = $extensionFactory;
        $this->orderFactory = $orderFactory;
    }

    /**
     * Set "BC_Pdfinvoice" to order data
     *
     * @param OrderRepositoryInterface $subject
     * @param OrderSearchResultInterface $searchResult
     *
     * @return OrderSearchResultInterface
     */
    public function setBCPdfInvoiceData(OrderInterface $order)
    {
        if ($order instanceof \Magento\Sales\Model\Order) {
            $myCustomOrderAttribute = $order->getBCPdfInvoice();
        } else {
            $orderModel = $this->orderFactory->create();
            $orderModel->load($order->getId());
            $myCustomOrderAttribute = $orderModel->getBCPdfInvoice();
        }

        $extensionAttributes = $order->getExtensionAttributes();
        $orderExtensionAttributes = $extensionAttributes ? $extensionAttributes
            : $this->orderExtensionFactory->create();
            
        $orderExtensionAttributes->setBCPdfInvoice($myCustomOrderAttribute);
        
        $order->setExtensionAttributes($orderExtensionAttributes);
    }
    
    /**
     * Add "BC_Pdfinvoice" extension attribute to order data object
     * to make it accessible in API data
     *
     * @param OrderRepositoryInterface $subject
     * @param OrderSearchResultInterface $searchResult
     *
     * @return OrderSearchResultInterface
     */
    public function afterGetList(
        OrderRepositoryInterface $subject,
        OrderSearchResultInterface $orderSearchResult
    ) {
        foreach ($orderSearchResult->getItems() as $order) {
            $this->setBCPdfInvoiceData($order);
        }
        return $orderSearchResult;
    }

    /**
     * Add "BC_Pdfinvoice" extension attribute to order data object
     * to make it accessible in API data
     *
     * @param OrderRepositoryInterface $subject
     * @param OrderInterface $order
     *
     * @return OrderInterface
     */
    public function afterGet(
        OrderRepositoryInterface $subject,
        OrderInterface $resultOrder
    ) {
        $this->setBCPdfInvoiceData($resultOrder);
        return $resultOrder;
    }
}

I removed 'generated', ran upgrade, compile, deploy commands, placed an order and tested the API call using Postman:

https://domain.com/rest/V1/orders?searchCriteria[filterGroups][0][filters][0][field]=entity_id&searchCriteria[filterGroups][0][filters][0][value]=17219&searchCriteria[filterGroups][0][filters][0][conditionType]=eq

The call is successful, but my custom attribute 'BC_Pdfinvoice' is not in the response.

The field was successfully created in the relevant database tables, as per my Vendor/Module/etc/db_schema.xml:

<?xml version="1.0"?>

<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
<table name="sales_order" resource="sales" engine="innodb" comment="Sales Order">
<column xsi:type="varchar" name="BC_Pdfinvoice" nullable="true" length="255" comment="Custom Order Attribute"/>
</table>
<table name="sales_order_grid" resource="sales" engine="innodb" comment="Sales Order">
<column xsi:type="varchar" name="BC_Pdfinvoice" nullable="true" length="255" comment="Custom Order Attribute"/>
</table>
<table name="quote" resource="checkout" engine="innodb" comment="Quote">
<column xsi:type="varchar" name="BC_Pdfinvoice" nullable="true" length="255" comment="Custom Order Attribute"/>
</table>
<table name="sales_invoice" resource="sales" engine="innodb" comment="Sales Invoice">
<column xsi:type="varchar" name="BC_Pdfinvoice" nullable="true" length="255" comment="Custom Order Attribute"/>
</table>
</schema>

Any help would be greatly appreciated :)

EDIT-

The attribute is now available in the response, albeit renamed somehow from "BC_Pdfinvoice" to "b_cpdfinvoice". Why does this happen? It's not in my code anywhere so it must be generated somewhere.

Regardless, I'm unable to update its value with a POST request. The JSON I am posting is:

{ "entity": { "b_cpdfinvoice": "1" } }

and the response is this:

"message": "Property \"BCpdfinvoice\" does not have accessor method \"getBCpdfinvoice\" in class \"Magento\\Sales\\Api\\Data\\OrderInterface\".",

Both getter and setter are available in the generated directory in both OrderExtension.php and OrderExtensionInterface.php:

 /**
     * @return string|null
     */
    public function getBCPdfinvoice()
    {
        return $this->_get('BC_Pdfinvoice');
    }

    /**
     * @param string $bCPdfinvoice
     * @return $this
     */
    public function setBCPdfinvoice($bCPdfinvoice)
    {
        $this->setData('BC_Pdfinvoice', $bCPdfinvoice);
        return $this;
    }

Although, another name change I don't get ^^ ($bCPdfinvoice).

6
  • The code looks good for me, I think $myCustomOrderAttribute is null due to which you are not seeing b_cpdfinvoice in the api response can you hard code some value for $myCustomOrderAttribute and check, If that works you can try $myCustomOrderAttribute = $myCustomOrderAttribute ?? ''; Commented Oct 27, 2022 at 18:20
  • Thanks, after hard coding $myCustomOrderAttribute value it is in the response. I'm confused though because the attribute name is BC_Pdfinvoice but it is showing up as b_cpdfinvoice, as you mentioned, why is that? Also, is it possible to POST values to it?
    – eleventy
    Commented Oct 27, 2022 at 19:23
  • @Moses Dinakaran What I'm trying to achieve is this: we have an ERP solution which needs to be able to post a pdf invoice so it's attached to a given order. The customer can then download it from their account.
    – eleventy
    Commented Oct 28, 2022 at 6:14
  • I am not sure exactly how your system is integrated with ERP, so not able to comment on that. In general BC_Pdfinvoice should have a url path of the PDF invoice that can be publicly accessible, this url can be of your server URL or cloud depends on your need while updating the order status from ERP you can update this BC_Pdfinvoice url value as well. The next part is the actual pdf invoice file, Since you say its generated in ERP you can write a shell script to sync this file from the ERP server to your Magento server or the cloud. Commented Oct 28, 2022 at 10:56
  • We have people dealing with that side, I just need to make sure this attribute is exposed and can be updated via REST API.
    – eleventy
    Commented Oct 28, 2022 at 12:36

1 Answer 1

1

You need to pass the extension attribute as the below in the post data.

{
  "entity":{
    "entity_id":1,
    "extension_attributes" : {
        "bc_pdfinvoice":"pdfurl"
    }
  }
}

Also you need to create a beforeSave Plugin and assign the bc_pdfinvoice value that you receive from the extension_attribues post data to the order object

public function beforeSave(\Magento\Sales\Api\OrderRepositoryInterface $subject,$order)
    {
        $extensionAttribute = $order->getExtensionAttributes();
        $pdfInvoice = $extensionAttribute->getBcPdfinvoice();
        $order->setBcPdfinvoice($pdfInvoice);
        return [$order];
    }

Try with the same attribute that have currently "b_cpdfinvoice", In case if this dosent work. I would suggest you to use the proper naming convention.

Rename "BC_Pdfinvoice" to "bc_pdfinvoice" both in extension_attributes.xml and "db_schema.xml"

Before doing this you also need to delete the current column that is already created in the below tables magento_sales_order_grid_archive,quote,sales_invoice,sales_order,sales_order_grid else you will face problem in setup upgrade.

Hope this helps, in case if you still face any problem let me know.

3
  • Many thanks for your help with this. I did as you say but I now get this in the response: "message": "Decoding error: \nUnable to unserialize value. Error: Syntax error\n#0 /home/****/public_html/vendor/magento/framework/Webapi/Rest/Request/Deserializer/Json.php(64)
    – eleventy
    Commented Oct 29, 2022 at 18:05
  • Sorry, my bad, payload typo. Fixed that, but there also seems an issue with the setter? As the response is now this: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'WHERE (entity_id=17233)' at line 1, query was: UPDATE sales_order SET WHERE (entity_id=17233)", i.e. nothing in the SET
    – eleventy
    Commented Oct 30, 2022 at 5:08
  • Again, another typo. This time in the AddCustomOrderAttribute.php. Jesus I need to stop that lol. All working now. Thanks again for your help!
    – eleventy
    Commented Oct 30, 2022 at 5:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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