smemo

技術メモです。

【Dynamics】フォームにSharepointドキュメントの表示

Sharepointドキュメントをフォーム上に表示できるとの記事を見つけたので試してみました。
※おそらくサポートされていない手法です。

jlattimer.blogspot.jp

最初に実現イメージは以下です。フォームにSharepointドキュメントが表示されます。f:id:smemo:20170110225516p:plain

※アップロード等もフォームから実施することができました。

以上です。

【Dynamics】WebAPIから操作(Action)を実行する

WebAPIで操作(Action)を実行する方法のメモ。
今回は営業案件をコピーする操作を作成し、その操作をWebAPIから実行します。

①まず操作を作成します。イメージは以下のような感じです。
f:id:smemo:20170108215419p:plain
f:id:smemo:20170108215427p:plain

②この作成した操作を実行するスクリプトを作成します。

var ActionFromJsSample = (function () {
    return {
        CopyAnken: function () {
            var id = Xrm.Page.data.entity.getId().replace("{", "").replace("}", "");

            ActionFromJsSample.ExecuteAction("opportunities", id, "Microsoft.Dynamics.CRM.cc_OpportuinityCopy")
            .then(function (result) {
                var name = "opportunity";
                var id = result.opportunityid;
                var windowOptions = {
                    openInNewWindow: true
                };

                Xrm.Utility.openEntityForm(name, id, null, windowOptions);
            })
            .catch(function (error) {
                Xrm.Utility.alertDialog(error);
            });
        },
        ExecuteAction: function (entitySetName, id, query) {
            return new Promise(function (resolve, reject) {
                var req = new XMLHttpRequest();
                req.open("POST", encodeURI("/api/data/v8.2/" + entitySetName + "(" + id + ")/" + query), true);
                req.setRequestHeader("Accept", "application/json");
                req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
                req.setRequestHeader("OData-MaxVersion", "4.0");
                req.setRequestHeader("OData-Version", "4.0");
                req.onreadystatechange = function () {
                    if (this.readyState == 4 /* complete */) {
                        req.onreadystatechange = null;
                        if (this.status >= 200 || this.status <= 204) {
                            resolve(JSON.parse(this.responseText));
                        }
                        else {
                            reject(this.responseText);
                        }
                    }
                };
                req.send();
            });
        }
    };
})();

③実行結果
コピー元の画面
f:id:smemo:20170108224837p:plain
実行後の画面
f:id:smemo:20170108224855p:plain
ちゃんとコピーされていることが確認できました。

【Dynamics】Functionを利用したWebAPIクエリの実行

WebAPIを使用してエンティティレコードの検索ができます。その検索にFunctionを使うことでより高度な検索条件を指定することが可能になります。
今回は例として、階層構造となっているエンティティの階層データの取得をしてみます。
※特定の取引先企業を指定してその配下の取引先企業を取得するようなクエリを実行してみます。

今のところ使えるクエリは以下のURLに記載されているものだそうです。
Web API Query Function Reference

その中から今回はUderOrEqual関数を使用します。
UnderOrEqual Function

WebAPIのイメージ

// 
https://orgname.crm7.dynamics.com/api/data/v8.2/accounts?$filter=Microsoft.Dynamics.CRM.UnderOrEqual(PropertyName='accountid',PropertyValue='7ccb8ce3-d1d4-e611-80fd-c4346bad16b0')

※この関数を使用するにあたって、第一引数はparentaccountid(親取引先企業)ではなくaccountid(取引先企業のGUID)を指定する必要があったところが少し混乱しました。

簡単な実装イメージ

取引先企業エンティティに階層構造となるデータを作成

f:id:smemo:20170108162046p:plain

取引先企業の最上位レコード(親企業の設定されていない企業)を表示
f:id:smemo:20170108162440p:plain

取引先企業を選択(クリック)するとその配下の取引先企業をWebAPIで取得して表示。子の企業が表示されているのがわかると思います。
f:id:smemo:20170108163048p:plain

他にもいろいろな検索条件が使用できるので複雑なクエリを実行する場合には使えるかと思います。

【Dynamics】Xrm.Page.ui.navigation アイテムについて(フォームのナビゲーション関連の操作)

まず、ナビゲーションとはこの部分を指します。

フォームのカスタマイズ画面でいうと
f:id:smemo:20170103221506p:plain
実際のフォーム画面でいうと
f:id:smemo:20170103221617p:plain

この部分に関連する操作の中で今回は「setFocus」というメソッドを使ってみます。
https://msdn.microsoft.com/ja-jp/library/gg309606.aspx#BKMK_setFocus

この「setFocus」というメソッドを使うことでナビゲーションをクリックしたときと同じような挙動、つまり関連ビューの表示をすることが可能になります。

実際のコード

var NavigationSample = (function () {
    return {
        SetFocusSample: function () {
            // ナビゲーションアイテムをすべて取得(サンプル)
            Xrm.Page.ui.navigation.items.forEach(
                            function (control, index) {
                                if (control.getId() === "navActivities") {
                                    // 活動関連ビューを表示
                                    Xrm.Page.ui.navigation.items.get("navActivities").setFocus();
                                }
                            }
                        )
        }
    };
})();


実行後の画面
f:id:smemo:20170103222258p:plain

通常だと、フォームの関連メニュークリック>関連ビュー表示という2クリック必要ですがこの機能をコマンドバーなどに実装しておけば1クリックで関連ビューが表示できそうです。(あまり有用ではないかもしれませんが。。)

【Dynamics】検索コントロール(Lookupフィールド)にフィルターを適用

ある特定のフォームだけLookupフィールドの検索条件を変更したい場合の対応方法。


通常Lookupフィールドに表示するレコードは「検索ダイアログ ボックス ビュー」で定義しますが、addCustomFilterというメソッドを使用することでLookupフィールドに表示するレコードを絞り込むことが可能になります。
https://msdn.microsoft.com/ja-jp/library/gg334266.aspx#BKMK_addCustomFilter


サンプルとして、取引先担当者の「電子メールを許可しない」フィールドが許可のみの取引先担当者を抽出するようなスクリプトを実装してみようと思います。


取引先担当者のレコードの状態
f:id:smemo:20170103204946p:plain

コード

var AddCustomFilterSample = (function () {
    return {
        // Onloadイベントに設定
        SetFilter: function () {
            Xrm.Page.getControl("parentcontactid").addPreSearch(AddCustomFilterSample.Filter);
        },
        // フィルターの設定
        Filter: function () {
            var filter = "<filter type='and'>" +
                         "<condition attribute='donotemail' operator='eq' value='false' />" +
                         "</filter>";
            Xrm.Page.getControl("parentcontactid").addCustomFilter(filter, "contact");
        }
    };
})();

実行結果:取引先担当者が絞り込まれています。
f:id:smemo:20170103204939p:plain


今回はOnloadイベントでフィルターを設定しましたが、フィールドの特定の値によってフィルターを動的に変更してUXの向上することができそうと感じました。

【Dynamics】WebAPI 代替キーを使用してクエリ

WebAPIでデータをクエリするときに、代替キーを設定しているエンティティでは代替キーを指定してクエリすることが可能。その方法についてメモ。

コード

// accountnumberが代替キーで設定したフィールド
https://org.api.crm7.dynamics.com/api/data/v8.2/accounts(accountnumber='A001')
// 取得フィールドの絞り込みも可
https://org.api.crm7.dynamics.com/api/data/v8.2/accounts(accountnumber='A004')?$select=name,donotpostalmail,_ownerid_value
// 関連エンティティの取得も可
https://org.api.crm7.dynamics.com/api/data/v8.2/accounts(accountnumber='A004')?$select=name&$expand=primarycontactid($select=emailaddress1)
注意点

代替キーを使用したクエリでは、検索結果が0件の場合に404エラーが返却されるので注意が必要です。

【Dynamics】オートコンプリートの実装

フィールドに値を入力する際のオートコンプリート(補完機能)の実装方法のメモ

以下MSDNを参考にしました。
https://msdn.microsoft.com/ja-jp/library/gg334266.aspx#BKMK_AutoCompletion


オートコンプリートのイメージ
業種コードに入力をすると候補を表示

f:id:smemo:20170102203809p:plain

全角入力時は入力後、スペースなどを入力すると候補が表示。。。(いまいちですね。。)
f:id:smemo:20170102204233p:plain


サンプルコードは以下

var OnKeyPressSample = (function () {

    return {
        Onload: function () {
            // List of sample account names to suggest
            sicCodes = [
              { name: 'G01', code: 'A01' },
              { name: 'G02', code: 'A02' },
              { name: 'G03', code: 'A03' },
              { name: 'G10', code: 'A04' },
              { name: 'G11', code: 'A05' },
              { name: 'G12', code: 'A06' },
              { name: '業種コード1', code: 'A07' },
            ];

            // キープレス時動作
            var keyPressFcn = function (ext) {
                try {
                    var userInput = Xrm.Page.getControl("sic").getValue();
                    resultSet = {
                        results: new Array(),
                        commands: {
                            id: "sp_commands",
                            label: "Learn More",
                            action: function () {
                                // "Learn More" link をクリックしたときのイベント
                                window.open("http://www.microsoft.com/en-us/dynamics/crm-customer-center/create-or-edit-an-account.aspx");
                            }
                        }
                    };

                    var userInputLowerCase = userInput.toLowerCase();

                    for (i = 0; i < sicCodes.length; i++) {
                        if (userInputLowerCase === sicCodes[i].name.substring(0, userInputLowerCase.length).toLowerCase()) {
                            resultSet.results.push({
                                id: i,
                                fields: [sicCodes[i].name]
                            });
                        }
                        if (resultSet.results.length >= 10) break;
                    }

                    if (resultSet.results.length > 0) {
                        ext.getEventSource().showAutoComplete(resultSet);
                    } else {
                        ext.getEventSource().hideAutoComplete();
                    }
                } catch (e) {
                    console.log(e);
                }
            };

            // 業種コードキープレスイベントに動作割り当て
            Xrm.Page.getControl("sic").addOnKeyPress(keyPressFcn);
        }
    };
})();

この機能を使って、ユーザーの入力を補助することが可能になりそうです。
(定型文入力+フリー入力などで使えそうでしょうか?)