Dynamics 365為子網格添加按鈕並獲取選擇記錄的信息


我是微軟Dynamics 365 & Power Platform方面的工程師/顧問羅勇,也是2015年7月到2018年6月連續三年Dynamics CRM/Business Solutions方面的微軟最有價值專家(Microsoft MVP),歡迎關注我的微信公眾號 MSFTDynamics365erLuoYong ,回復426或者20201103可方便獲取本文,同時可以在第一間得到我發布的最新博文信息,follow me!

前面的博文 Dynamics 365定制:在實體的列表界面添加按鈕 講述了實體列表界面添加按鈕,博文 Dynamics 365中的導出至Excel,批量編輯,導入權限及限制導出至Excel 講述了修改Application Ribbon來定制命令欄模板以達到修改實體公共按鈕的目的,今天我們來講述下修改子網格(Subgrid)的按鈕。

我假設一個需求,如下界面,我想在Sales Order Item實體的子網格命令欄添加一個【Create Invoices】按鈕,點擊后將選中的Sales Order Items批量插入到下面的Invoices子網格中。

  

好的,基本的我不解說了,就將設置的內容撿重點說一下。新增一個按鈕放到最前面,這個按鈕對應的command如下,Custom JavaScript Action對應的 Libray為 $webresource:ly_/scripts/salesorderitem/salesorderitem.js ,Function Name為 LuoYong.SalesOrderItem.CreateInvoiceAction ,傳遞了PrimaryControl 和 SelectedControl 這兩個Crm Parameter 給執行的函數。還關聯了一個Enable Rule。

 

 這個Enable Rule的定義如下,注意要讓這個按鈕一直顯示的話,記得添加一個 SelectionCountRule. 一般我還會添加一個FormStateRule。

我這里添加了一個Custom Rule,使用的Library還是 $webresource:ly_/scripts/salesorderitem/salesorderitem.js,執行的FunctionName是 LuoYong.SalesOrderItem.CreateInvoiceEnableRule

其實這個Function的功能很簡單,看表單對應的實體Sales Order上的statuscode是否為Approved。你會問為啥不直接用 Value Rule,原因是不行啊。

  

我這里使用的代碼如下:

"use strict";
var LuoYong = window.LuoYong || {};
LuoYong.SalesOrderItem = LuoYong.SalesOrderItem || {};
(function () {
    var getRandomString = function (len, charSet) {
        charSet = charSet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        var randomString = '';
        for (var i = 0; i < len; i++) {
            var randomPoz = Math.floor(Math.random() * charSet.length);
            randomString += charSet.substring(randomPoz, randomPoz + 1);
        }
        return randomString;
    };
    var executeBatch = function (clientURL, batchId, requestMsg, successCallback, errorCallback) {
        var req = new XMLHttpRequest()
        req.open("POST", encodeURI(clientURL + "/api/data/v9.1/$batch", true));//true是異步請求,false是同步請求
        req.setRequestHeader("Content-Type", "multipart/mixed;boundary=batch_" + batchId);
        req.setRequestHeader("Accept", "application/json");
        req.setRequestHeader("OData-MaxVersion", "4.0");
        req.setRequestHeader("OData-Version", "4.0");
        req.onreadystatechange = function () {
            if (this.readyState == 4) {
                req.onreadystatechange = null;
                if (this.status == 200) {
                    successCallback(this.responseText);
                }
                else {
                    errorCallback(this.responseText);
                }
            }
        };
        req.send(requestMsg);
    }

    this.CreateInvoiceAction = function (primaryControl, selectedControl) {
        var formContext = primaryControl;
        console.log("當前表單記錄的ID=" + formContext.data.entity.getId());
        var entityId = formContext.data.entity.getId().replace('{', '').replace('}','');
        var entityPrimaryAttrValue = formContext.data.entity.getPrimaryAttributeValue();
        //獲取子網格選中的記錄,最多一次選中250行
        var selectedRows = selectedControl.getGrid().getSelectedRows();
        console.log("共選擇了" + selectedRows.getLength() + "條記錄!");
        if (selectedRows.getLength() === 0) {
            Xrm.Navigation.openErrorDialog({ message: "請選擇至少一條記錄!" }).then(
                function (success) {
                    return;
                },
                function (error) {
                    console.log(error);
                });
        }
        else {
            var clientURL = Xrm.Utility.getGlobalContext().getClientUrl();
            var batchId = getRandomString(12);
            var changesetId = getRandomString(12);
            var requestMsg = ["--batch_" + batchId];
            requestMsg.push("Content-Type: multipart/mixed;boundary=changeset_" + changesetId);
            requestMsg.push("");
            selectedRows.forEach(function (row, i) {
                //通過下面這種方式可以獲取到子網格顯示的列的值,但是不能獲取到子網格沒有顯示的列的值
                //如果要獲取就要額外自己查詢了,或者顯示到子網格中
                console.log(row.getData().getEntity().attributes.get("ly_quantity").getValue());
                //獲取選擇記錄的ID
                console.log(row.getData().getEntity().getId());
                //獲取選擇記錄的實體邏輯名稱
                console.log(row.getData().getEntity().getEntityName());
                //獲取選擇記錄的主屬性的值
                console.log(row.getData().getEntity().getPrimaryAttributeValue());

                requestMsg.push("--changeset_" + changesetId);
                requestMsg.push("Content-Type: application/http");
                requestMsg.push("Content-Transfer-Encoding:binary");
                requestMsg.push("Content-ID: " + (i + 1).toString());
                requestMsg.push("");
                requestMsg.push("POST " + clientURL + "/api/data/v9.1/ly_luoyonginvoices HTTP/1.1");
                requestMsg.push("Content-Type: application/json;type=entry");
                requestMsg.push("");//注意這里要加空行
                var createData =
                {
                    "ly_name": entityPrimaryAttrValue + "-" + row.getData().getEntity().attributes.get("ly_itemno").getValue(),
                    "ly_quantity": row.getData().getEntity().attributes.get("ly_quantity").getValue(),
                    "ly_totalvalue": row.getData().getEntity().attributes.get("ly_totalvalue").getValue(),
                    "ly_unitprice": row.getData().getEntity().attributes.get("ly_unitprice").getValue(),
                    "ly_salesorderid@odata.bind": "/ly_luoyongsalesorders(" + entityId + ")",
                    "ly_salesorderitemid@odata.bind": "/ly_luoyongsalesorderitems(" + row.getData().getEntity().getId().replace('{', '').replace('}', '') + ")",
                };
                requestMsg.push(JSON.stringify(createData));
            });
            requestMsg.push("--changeset_" + changesetId + "--");//changeset結束這里前面不要加空行
            requestMsg.push("");//注意這里要加空行
            requestMsg.push("--batch_" + batchId + "--");//batch結束前面加空行
            executeBatch(clientURL, batchId, requestMsg.join("\n"), function (responseText) {
                var alertStrings = { confirmButtonLabel: "Yes", text: "Done!", title: "Info" };
                var alertOptions = { height: 120, width: 260 };
                Xrm.Navigation.openAlertDialog(alertStrings, alertOptions).then(
                    function (success) {
                        //完成后刷新當前子網格,以便取消之前選擇的記錄,可能還有其他邏輯會讓處理的記錄不顯示
                        selectedControl.refresh();
                        //完成后刷新子網格顯示新建數據
                        var gridContext = formContext.getControl("subgridInvoices"); 
                        gridContext.refresh();
                    },
                    function (error) {
                        console.log(error.message);
                    }
                );
            }, function (responseText) {
                Xrm.Navigation.openErrorDialog({ message: "Create invoices error. " + responseText});
            });

        }
    };
    this.CreateInvoiceEnableRule = function (primaryControl) {
        var formContext = primaryControl;
        return formContext.getAttribute("statuscode").getValue() === 364750000;
    };
}).call(LuoYong.SalesOrderItem);

 

演示下,選擇記錄后,點擊【Create Invoices】按鈕。

  

提示操作完成,速度很快的,因為是用web api執行批量操作,一次請求創建多條記錄。關於使用Web API執行批量操作請參考我的博文 使用JS通過Web API執行批量操作,多個操作是一個事務! 。

 

上面的提示點擊【Yes】后可以看到兩個子網格刷新了,下面的Invoices子網格可以看到新數據了,上面子網格之前選擇的記錄也不選擇了(其實也是刷新了的緣故)。 

 

然后你可能會問,如果我需要不在一個事務中呢?比如我選三條記錄來操作,有兩條成功就成功,一條失敗就失敗,不互相影響,我目前找到的方法的代碼如下:

"use strict";
var LuoYong = window.LuoYong || {};
LuoYong.SalesOrderItem = LuoYong.SalesOrderItem || {};
(function () {
    var getRandomString = function (len, charSet) {
        charSet = charSet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        var randomString = '';
        for (var i = 0; i < len; i++) {
            var randomPoz = Math.floor(Math.random() * charSet.length);
            randomString += charSet.substring(randomPoz, randomPoz + 1);
        }
        return randomString;
    };
    var executeBatch = function (clientURL, batchId, requestMsg, successCallback, errorCallback) {
        var req = new XMLHttpRequest()
        req.open("POST", encodeURI(clientURL + "/api/data/v9.1/$batch", true));//true是異步請求,false是同步請求
        req.setRequestHeader("Content-Type", "multipart/mixed;boundary=batch_" + batchId);
        req.setRequestHeader("Accept", "application/json");
        req.setRequestHeader("OData-MaxVersion", "4.0");
        req.setRequestHeader("OData-Version", "4.0");
        req.onreadystatechange = function () {
            if (this.readyState == 4) {
                req.onreadystatechange = null;
                if (this.status == 200) {
                    successCallback(this.responseText);
                }
                else {
                    errorCallback(this.responseText);
                }
            }
        };
        req.send(requestMsg);
    }

    this.CreateInvoiceAction = function (primaryControl, selectedControl) {
        var formContext = primaryControl;
        console.log("當前表單記錄的ID=" + formContext.data.entity.getId());
        var entityId = formContext.data.entity.getId().replace('{', '').replace('}', '');
        var entityPrimaryAttrValue = formContext.data.entity.getPrimaryAttributeValue();
        //獲取子網格選中的記錄,最多一次選中250行
        var selectedRows = selectedControl.getGrid().getSelectedRows();
        console.log("共選擇了" + selectedRows.getLength() + "條記錄!");
        if (selectedRows.getLength() === 0) {
            Xrm.Navigation.openErrorDialog({ message: "請選擇至少一條記錄!" }).then(
                function (success) {
                    return;
                },
                function (error) {
                    console.log(error);
                });
        }
        else {
            var clientURL = Xrm.Utility.getGlobalContext().getClientUrl();
            var batchId = getRandomString(12);
            var changesetId = getRandomString(12);
            var requestMsg = [];
            selectedRows.forEach(function (row, i) {
                //通過下面這種方式可以獲取到子網格顯示的列的值,但是不能獲取到子網格沒有顯示的列的值
                //如果要獲取就要額外自己查詢了,或者顯示到子網格中
                console.log(row.getData().getEntity().attributes.get("ly_quantity").getValue());
                //獲取選擇記錄的ID
                console.log(row.getData().getEntity().getId());
                //獲取選擇記錄的實體邏輯名稱
                console.log(row.getData().getEntity().getEntityName());
                //獲取選擇記錄的主屬性的值
                console.log(row.getData().getEntity().getPrimaryAttributeValue());

                requestMsg.push("--batch_" + batchId);
                requestMsg.push("Content-Type: multipart/mixed;boundary=changeset_" + changesetId);
                requestMsg.push("");
                requestMsg.push("--changeset_" + changesetId);
                requestMsg.push("Content-Type: application/http");
                requestMsg.push("Content-Transfer-Encoding:binary");
                requestMsg.push("Content-ID: " + (i + 1).toString());
                requestMsg.push("");
                requestMsg.push("POST " + clientURL + "/api/data/v9.1/ly_luoyonginvoices HTTP/1.1");
                requestMsg.push("Content-Type: application/json;type=entry");
                requestMsg.push("");//注意這里要加空行
                var createData =
                {
                    "ly_name": entityPrimaryAttrValue + "-" + row.getData().getEntity().attributes.get("ly_itemno").getValue(),
                    "ly_quantity": row.getData().getEntity().attributes.get("ly_quantity").getValue(),
                    "ly_totalvalue": row.getData().getEntity().attributes.get("ly_totalvalue").getValue(),
                    "ly_unitprice": row.getData().getEntity().attributes.get("ly_unitprice").getValue(),
                    "ly_salesorderid@odata.bind": "/ly_luoyongsalesorders(" + entityId + ")",
                    "ly_salesorderitemid@odata.bind": "/ly_luoyongsalesorderitems(" + row.getData().getEntity().getId().replace('{', '').replace('}', '') + ")",
                };
                requestMsg.push(JSON.stringify(createData));
                requestMsg.push("--changeset_" + changesetId + "--");//changeset結束這里前面不要加空行
                requestMsg.push("");//注意這里要加空行
            });
            requestMsg.push("--batch_" + batchId + "--");//batch結束前面加空行
            executeBatch(clientURL, batchId, requestMsg.join("\n"), function (responseText) {
                var alertStrings = { confirmButtonLabel: "Yes", text: "Done!", title: "Info" };
                var alertOptions = { height: 120, width: 260 };
                Xrm.Navigation.openAlertDialog(alertStrings, alertOptions).then(
                    function (success) {
                        //完成后刷新當前子網格,以便取消之前選擇的記錄,可能還有其他邏輯會讓處理的記錄不顯示
                        selectedControl.refresh();
                        //完成后刷新子網格顯示新建數據
                        var gridContext = formContext.getControl("subgridInvoices");
                        gridContext.refresh();
                    },
                    function (error) {
                        console.log(error.message);
                    }
                );
            }, function (responseText) {
                //完成后刷新當前子網格,以便取消之前選擇的記錄,可能還有其他邏輯會讓處理的記錄不顯示
                selectedControl.refresh();
                //完成后刷新子網格顯示新建數據
                var gridContext = formContext.getControl("subgridInvoices");
                gridContext.refresh();
                Xrm.Navigation.openErrorDialog({ message: "Create invoices error. " + responseText });
            });
        }
    };
    this.CreateInvoiceEnableRule = function (primaryControl) {
        var formContext = primaryControl;
        return formContext.getAttribute("statuscode").getValue() === 364750000;
    };
}).call(LuoYong.SalesOrderItem);

 

看到的Network,請求內容如下:

--batch_cBqy7oDfV2Ok
Content-Type: multipart/mixed;boundary=changeset_t2pUnckorFaC

--changeset_t2pUnckorFaC
Content-Type: application/http
Content-Transfer-Encoding:binary
Content-ID: 1

POST https://org6485af04.crm5.dynamics.com/api/data/v9.1/ly_luoyonginvoices HTTP/1.1
Content-Type: application/json;type=entry

{"ly_name":"ORD-202003-000001-10","ly_quantity":10,"ly_totalvalue":1000,"ly_unitprice":100,"ly_salesorderid@odata.bind":"/ly_luoyongsalesorders(85B7FEF2-DB1D-EB11-A813-000D3AC71539)","ly_salesorderitemid@odata.bind":"/ly_luoyongsalesorderitems(28B60A06-DC1D-EB11-A813-000D3AC71539)"}
--changeset_t2pUnckorFaC--

--batch_cBqy7oDfV2Ok
Content-Type: multipart/mixed;boundary=changeset_t2pUnckorFaC

--changeset_t2pUnckorFaC
Content-Type: application/http
Content-Transfer-Encoding:binary
Content-ID: 2

POST https://org6485af04.crm5.dynamics.com/api/data/v9.1/ly_luoyonginvoices HTTP/1.1
Content-Type: application/json;type=entry

{"ly_name":"ORD-202003-000001-20","ly_quantity":20,"ly_totalvalue":4000,"ly_unitprice":200,"ly_salesorderid@odata.bind":"/ly_luoyongsalesorders(85B7FEF2-DB1D-EB11-A813-000D3AC71539)","ly_salesorderitemid@odata.bind":"/ly_luoyongsalesorderitems(CD75B411-DC1D-EB11-A813-000D3AC71539)"}
--changeset_t2pUnckorFaC--

--batch_cBqy7oDfV2Ok
Content-Type: multipart/mixed;boundary=changeset_t2pUnckorFaC

--changeset_t2pUnckorFaC
Content-Type: application/http
Content-Transfer-Encoding:binary
Content-ID: 3

POST https://org6485af04.crm5.dynamics.com/api/data/v9.1/ly_luoyonginvoices HTTP/1.1
Content-Type: application/json;type=entry

{"ly_name":"ORD-202003-000001-40","ly_quantity":40,"ly_totalvalue":16000,"ly_unitprice":400,"ly_salesorderid@odata.bind":"/ly_luoyongsalesorders(85B7FEF2-DB1D-EB11-A813-000D3AC71539)","ly_salesorderitemid@odata.bind":"/ly_luoyongsalesorderitems(09507D25-DC1D-EB11-A813-000D3AC71539)"}
--changeset_t2pUnckorFaC--

--batch_cBqy7oDfV2Ok--

 

如果有報錯的記錄,返回的內容示例如下,返回的statuscode為400:

--batchresponse_3ac10a59-4313-4db4-b6f0-ca247e37c5d5
Content-Type: multipart/mixed; boundary=changesetresponse_f7a0e798-8209-4fd5-beb0-869fffc8dc31

--changesetresponse_f7a0e798-8209-4fd5-beb0-869fffc8dc31
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1

HTTP/1.1 204 No Content
OData-Version: 4.0
Location: https://org6485af04.crm5.dynamics.com/api/data/v9.1/ly_luoyonginvoices(7a2ed124-f622-eb11-a813-000d3ac71539)
OData-EntityId: https://org6485af04.crm5.dynamics.com/api/data/v9.1/ly_luoyonginvoices(7a2ed124-f622-eb11-a813-000d3ac71539)


--changesetresponse_f7a0e798-8209-4fd5-beb0-869fffc8dc31--
--batchresponse_3ac10a59-4313-4db4-b6f0-ca247e37c5d5
Content-Type: multipart/mixed; boundary=changesetresponse_195a9cce-b6eb-4edc-b5ff-44d5f2dc8870

--changesetresponse_195a9cce-b6eb-4edc-b5ff-44d5f2dc8870
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 2

HTTP/1.1 204 No Content
OData-Version: 4.0
Location: https://org6485af04.crm5.dynamics.com/api/data/v9.1/ly_luoyonginvoices(7c2ed124-f622-eb11-a813-000d3ac71539)
OData-EntityId: https://org6485af04.crm5.dynamics.com/api/data/v9.1/ly_luoyonginvoices(7c2ed124-f622-eb11-a813-000d3ac71539)


--changesetresponse_195a9cce-b6eb-4edc-b5ff-44d5f2dc8870--
--batchresponse_3ac10a59-4313-4db4-b6f0-ca247e37c5d5
Content-Type: multipart/mixed; boundary=changesetresponse_7cb33294-b5bc-4557-a4a3-78fdd4bfe937

--changesetresponse_7cb33294-b5bc-4557-a4a3-78fdd4bfe937
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 3

HTTP/1.1 400 Bad Request
REQ_ID: c26fba38-ba4e-427f-9ac6-b01733e7eb70
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0

{"error":{"code":"0x80040265","message":"Quantity should not greater than 30!"}}
--changesetresponse_7cb33294-b5bc-4557-a4a3-78fdd4bfe937--
--batchresponse_3ac10a59-4313-4db4-b6f0-ca247e37c5d5--

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM