intacct / intacct-sdk-js

Official repository of the Sage Intacct SDK for JavaScript in Node.js
https://developer.intacct.com/tools/sdk-node-js/
Apache License 2.0
23 stars 33 forks source link

Error with AccountsReceivable.CustomerUpdate but not in XML API #74

Open almercier opened 2 years ago

almercier commented 2 years ago

I am attempting to follow the example to update a customer record to simply change the "comments" field via the example provided here https://github.com/Intacct/intacct-sdk-node-js-examples/blob/master/crud-customer.js

However it keeps throwing "BL04002055 Smart Rule CUST_REQ_TAXABLE Violated The Taxable field (Additional info tab) must be checked. [Support ID: 7lGI7EB030%7EYTuKDP0g2Pw--dgW6G9JxgAAAAI]". I'm not setting TAXABLE or any fields that would affect this smart rule. And this does not error when I use the Update Customer XML API directly.

My code

const updateFn = new IA.Functions.AccountsReceivable.CustomerUpdate();
updateFn.customerId = 'TESTCUSTOMER',
updateFn.comments = 'test - ' + Math.floor(Math.random() * 1000);          
const updateRes = await client.execute(updateFn);

However, following the documentation here https://developer.intacct.com/api/accounts-receivable/customers/#update-customer I'm able to successfully update the customer as expected using the raw XML request in postman. Confirmed by checking the comments field on the record after executing the call.

<function controlid="testFunctionId">
    <update>
        <CUSTOMER>
            <CUSTOMERID>TESTCUSTOMER</CUSTOMERID>
            <COMMENTS>test - 1234</COMMENTS>
        </CUSTOMER>
    </update>
</function>

ALSO why does the CustomerUpdate() function not support being able to reference customer by RECORDNO when either RECORDNO or CUSTOMERID is supported in the XML API?

almercier commented 2 years ago

Just noticed https://github.com/Intacct/intacct-sdk-js/issues/26

Which explains why the smart rule is getting thrown, since the fn seems to be including the "TAXABLE" XML tag even though I am not specifying taxable.

Which tells me that it's trying to clear every field that I don't explicitly specify on the update function. Wouldn't that mean that the example here would set active to true, but also unintentionally clear out every single other field??

almercier commented 2 years ago

image

it's because writeElement looks for the value to be explicitly null, to determine if it should use the "writeNull" param. Since I'm not specifying that property, it's undefined (not null) and ends up writing the empty tag regardless of the "writeNull" param being set to false.

almercier commented 2 years ago

I'm getting around it now with this simple CRUD custom function, which IMO is actually easier to use (albeit more loose)

class ObjectCRUD extends IA.Functions.AbstractFunction {
    constructor(objectName, method, object) {
        super();
        this._objectName = objectName;
        this._method = method;
        this._object = object;
    }

    writeXml (xml) {
        xml.writeStartElement("function");
        xml.writeAttribute("controlid", this.controlId, true);
        xml.writeStartElement(this._method);

        if (method === 'delete') {
            xml.writeElement('object', this._objectName);
            xml.writeElement('keys', this._object);
        }
        else {
            // insert or update
            xml.writeStartElement(this._objectName);
            for (let key in this._object) {
                if (this._object.hasOwnProperty(key)) {
                    xml.writeElement(key, this._object[key]);
                }
            }
            xml.writeEndElement(); // object name
        }

        xml.writeEndElement(); // method
        xml.writeEndElement(); // function
    };
}

intacctApi.update = function (objectName, object) {
    let action = new ObjectCRUD(objectName, 'update', object);   
    return intacctApi.IAClient.execute(action);
}

intacctApi.insert = function (objectName, object) {
    let action = new ObjectCRUD(objectName, 'create', object);   
    return intacctApi.IAClient.execute(action);
}

intacctApi.delete = function (objectName, recordNo) {
    let action = new ObjectCRUD(objectName, 'delete', recordNo);   
    return intacctApi.IAClient.execute(action);
}

Such that I can then just call

const updateRes = await intacctApi.update('CUSTOMER', {
    CUSTOMERID: 'TESTCUSTOMER',
    COMMENTS: 'test new comment',
});