0

I am fairly new to CRM Web API and coding in general but I did want to try my hand at this regardless.

I was trying to implement a custom "convert Contract to Invoice" functionality by first creating an Invoice with the data from a contract and secondly create an InvoiceDetail from ContractDetail/lines, which kind of works via httprequests, though very quickly I have noticed that requesting and posting data in such a way is very limited. I've read that its possible to work with more data by utilizing batches, however I am not really sure how to convert my code to batch appropriately.

I'd really appreciate any kind of help in that regard!

function retrieveProducts(data, newInvoiceID) {
    var data = data;
    var ID = newInvoiceID;

    for (var i = 0; i < data.length; i++) {
        var existingProduct = data[i];

        var lineitem = {};
        lineitem["productid@odata.bind"] = "/products(" + existingProduct._productid_value + ")";
        lineitem["uomid@odata.bind"] = "/uoms(" + existingProduct._uomid_value + ")";
        lineitem["new_contractactiveon"] = existingProduct.activeon;
        lineitem["new_contractexpireson"] = existingProduct.expireson;
        lineitem.quantity = existingProduct.initialquantity;
        lineitem["invoiceid@odata.bind"] = "/invoices("+ ID +")";
        createRecords(lineitem);
    }
}

    function createRecords(data) {
    var req = new XMLHttpRequest();
req.open("POST", Xrm.Page.context.getClientUrl() + "/api/data/v8.2/invoicedetails", false);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.onreadystatechange = function() {
    if (this.readyState === 4) {
        req.onreadystatechange = null;
        if (this.status === 204) {
            var uri = this.getResponseHeader("OData-EntityId");
            var regExp = /\(([^)]+)\)/;
            var matches = regExp.exec(uri);
            var newEntityId = matches[1];
        } else {
            Xrm.Utility.alertDialog(this.statusText);
        }
    }
};
req.send(JSON.stringify(data));
}

EDIT: Here the full code

function start() {
    var ID = parent.Xrm.Page.data.entity.getId().substring(1, 37);
    var fetchxml = `

<fetch distinct="false" mapping="logical" output-format="xml-platform" version="1.0">
<entity name="contractdetail">
<attribute name="title"/>
<attribute name="productid"/>
<attribute name="allotmentsremaining"/>
<attribute name="contractdetailid"/>
<attribute name="net"/>
<attribute name="activeon"/>
<attribute name="serviceaddress"/>
<attribute name="initialquantity"/>
<attribute name="customerid"/>
<attribute name="expireson"/>
<attribute name="uomid"/>
<attribute name="new_ortfirma"/>
<order descending="false" attribute="title"/>
<link-entity name="contract" alias="ab" to="contractid" from="contractid">
<filter type="and">
<condition attribute="contractid" value="{` + ID + `}" uitype="contract" operator="eq"/>
</filter>
</link-entity>
</entity>
</fetch>`;

    var encodedFetchXML = encodeURIComponent(fetchxml);
    var queryPath = "/api/data/v8.2/contractdetails?fetchXml=" + encodedFetchXML;
    var requestPath = parent.Xrm.Page.context.getClientUrl() + queryPath;

    var req = new XMLHttpRequest();
    req.open("GET", requestPath, true);
    req.setRequestHeader("Accept", "application/json");
    req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    req.setRequestHeader("Prefer", "odata.include-annotations=\"*\"");
    req.onreadystatechange = function ()

    {

        if (this.readyState === 4)

        {

            this.onreadystatechange = null;
            if (this.status === 200)

            {
                var returned = JSON.parse(this.responseText);
                var results = returned.value;
                
                getContractInfo(results);
            } else

            {

                alert(this.statusText);

            }

        }

    };
 
    req.send();
    window.alert("Invoice created.");
    setStatus(ID);
}

function retrieveProducts(data, newInvoiceID) {
    var data = data;
    var ID = newInvoiceID;

    for (var i = 0; i < data.length; i++) {
        var existingProduct = data[i];

        var lineitem = {};
        lineitem["productid@odata.bind"] = "/products(" + existingProduct._productid_value + ")";
        lineitem["uomid@odata.bind"] = "/uoms(" + existingProduct._uomid_value + ")";
        lineitem["new_contractactiveon"] = existingProduct.activeon;
        lineitem["new_contractexpireson"] = existingProduct.expireson;
        lineitem["new_ortfirma@odata.bind"] = "/accounts(" + existingProduct._new_ortfirma_value + ")";
        lineitem.quantity = existingProduct.initialquantity;
        lineitem["invoiceid@odata.bind"] = "/invoices("+ ID +")";
        createRecords(lineitem);
    }
}

function setStatus(ID) {
var entity = {};
entity.statecode = 1;
entity.statuscode = 2;

var req = new XMLHttpRequest();
req.open("PATCH", parent.Xrm.Page.context.getClientUrl() + "/api/data/v8.2/contracts(ED9C4959-A951-EB11-BC99-00155DB20709)", true);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.onreadystatechange = function() {
    if (this.readyState === 4) {
        req.onreadystatechange = null;
        if (this.status === 204) {
            //Success - No Return Data - Do Something
        } else {
            parent.Xrm.Utility.alertDialog(this.statusText);
        }
    }
};
req.send(JSON.stringify(entity));
}

function createRecords(data) {
    var req = new XMLHttpRequest();
req.open("POST", parent.Xrm.Page.context.getClientUrl() + "/api/data/v8.2/invoicedetails", false);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.onreadystatechange = function() {
    if (this.readyState === 4) {
        req.onreadystatechange = null;
        if (this.status === 204) {
            var uri = this.getResponseHeader("OData-EntityId");
            var regExp = /\(([^)]+)\)/;
            var matches = regExp.exec(uri);
            var newEntityId = matches[1];
        } else {
            parent.Xrm.Utility.alertDialog(this.statusText);
        }
    }
};
req.send(JSON.stringify(data));
}


//info from contact that is going to be used in the invoice
function getContractInfo(data) {
    var ID = parent.Xrm.Page.data.entity.getId().substring(1, 37);

    var req = new XMLHttpRequest();
    req.open("GET", parent.Xrm.Page.context.getClientUrl() + "/api/data/v8.2/contracts(" + ID + ")?$select=activeon,_customerid_value,expireson,_serviceaddress_value,title&$expand=contract_line_items($select=activeon,expireson,initialquantity,price,title)", true);
    req.setRequestHeader("OData-MaxVersion", "4.0");
    req.setRequestHeader("OData-Version", "4.0");
    req.setRequestHeader("Accept", "application/json");
    req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    req.setRequestHeader("Prefer", "odata.include-annotations=\"*\"");
    req.onreadystatechange = function () {
        if (this.readyState === 4) {
            req.onreadystatechange = null;
            if (this.status === 200) {
                var result = JSON.parse(this.response);
                var activeon = result["activeon"];
                var _customerid_value = result["_customerid_value"];
                var expireson = result["expireson"];
                var title = result["title"];

                createInvoice(_customerid_value, title, data, activeon, expireson);
            } else {
                parent.Xrm.Utility.alertDialog(this.statusText);
            }
        }
    };
    req.send();
}
//create the invoice
function createInvoice(customer, title, data, activeon, expireson) {

    var entity = {};
    entity["customerid_account@odata.bind"] = "/accounts(" + customer + ")";
    entity["pricelevelid@odata.bind"] = "/pricelevels(be2084c3-1e50-eb11-bc99-00155db20709)";
    entity.name = title;
    entity["transactioncurrencyid@odata.bind"] = "/transactioncurrencies(59f3fa86-385a-ea11-bc6a-00155db20709)";
    entity.new_contractactiveon = activeon;
    entity.new_contractexpireson = expireson;

    var req = new XMLHttpRequest();
    req.open("POST", parent.Xrm.Page.context.getClientUrl() + "/api/data/v8.2/invoices", true);
    req.setRequestHeader("OData-MaxVersion", "4.0");
    req.setRequestHeader("OData-Version", "4.0");
    req.setRequestHeader("Accept", "application/json");
    req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    req.onreadystatechange = function () {
        if (this.readyState === 4) {
            req.onreadystatechange = null;
            if (this.status === 204) {
                var uri = this.getResponseHeader("OData-EntityId");
                var regExp = /\(([^)]+)\)/;
                var matches = regExp.exec(uri);
                var newEntityId = matches[1];
                var ID = this.getResponseHeader("OData-EntityId").substring(67,103); 
                retrieveProducts(data, ID);

                
                
            } else {
                parent.Xrm.Utility.alertDialog(this.statusText);
            }
        }
    };
    req.send(JSON.stringify(entity));

}
uWuW
  • 1
  • 1
  • technically you are forced to use Batch when the url of a GET request is too long (like you are using a fetchxml) but from the code you posted that is not your case. Why exactly do you need a Batch request – Guido Preite Jan 14 '21 at 16:06
  • hi Guido Preite, first of all thank you for taking your time to answer! I managed to find the reason for the continuous bad request I was receiving: the attribute I was trying to insert was always undefined because of a field I missed to populate. Ergo there is no need for a batch request! :) Although I'd like to set the contract's status and statecode to "Invoiced" after my Invoice has been created. If I just add another PATCH Request at the very end of my start function I am receiving an "Internal Server Error" message. Is there a way to achieve this? – uWuW Jan 14 '21 at 20:13
  • @uWuW update your code in question to show PATCH request as well – Arun Vinoth-Precog Tech - MVP Jan 15 '21 at 22:20
  • I have updated my post – uWuW Jan 17 '21 at 15:35

0 Answers0