TOP >技術者情報 >JSプラグイン >案件登録画面において、集計のために案件商品に対して疑似的に担当社員・担当部署を紐づける

技術者情報 案件登録画面において、集計のために案件商品に対して疑似的に担当社員・担当部署を紐づける

必要な情報集計をするために、案件情報登録画面上で商品情報項目に対して、疑似的に担当社員・担当部署を紐づけするJSプラグインを紹介します。
このJSプラグインにより、案件情報登録画面上で、商品情報項目の担当・部署情報がテキスト項目型にセットされます。
データの登録更新処理にはREST APIを利用します。

ここではJSプラグインの実例や、プラグイン上で必要な値の確認方法を紹介します。


機能概要
案件登録画面の商品情報項目で「部署番号」、「部署名」、「部署番号」、「担当者名」が疑似的に紐付され登録できます。
部署用・担当者用の検索ボックスが設置され、検索結果が各テキストボックスに設定されます。
検索結果を選択後、エンターキー押下によってREST APIを利用した登録処理が行われます。 




項目対応表

 No.  名前  型
 1  商品名  テキストボックス
 2  部署番号  テキストボックス、ReadOnly
 3  部署名  テキストボックス、ReadOnly
 4  部署名  テキストボックス
 5  検索ボタン(部署名)  ボタン
 6  部署リスト  モーダルウィンドウ
 7  担当者番号  テキストボックス、ReadOnly
 8  担当者名  テキストボックス、ReadOnly
 9  担当者名  テキストボックス
 10  検索ボタン(担当者名)  ボタン
 11  担当者リスト  モーダルウィンドウ



事前設定  
営業プロセスの案件項目の変更画面上で、「案件商品情報」に制御用データを追加します。
No.4定義データは編集可能としておき、プラグイン側で編集不可とします。


※事前に商品情報項目に対して、部署番号・部署名・担当者番号・担当者名用の項目を、編集不可で用意しておく

 

マニフェストファイルの作成
JSプラグインの動作に必要なマニフェストファイルを作成します。
詳細はJSプラグインの作成マニュアルをご覧ください。
menu_typeについては、案件の新規登録画面であるため「2_007」です。

manifest.json
 {
    "plugin_name": {
        "ja": "サンプルプラグイン04",
        "en": "sample plugin04",
        "zh": "插件的例子04",
        "ko": "샘플 플러그인04"
    },
    "menu_type":0,
    "target" : ["2_007"],
    "note": {
        "ja": "案件商品に対して担当社員・担当部署を紐づける",
        "en": "Associate the employee in charge and the department in charge with the product",
        "zh": "将负责员工和主管部门与产品相关联",
        "ko": "안건 상품에 대해 담당 직원 · 부서를 끈 짓는"
    },
    "version":"ver 1.0",
    "author":"softbrain"

 
必要な値の確認
JSプラグインの動作に必要になる内部コードを確認します。
本機能で必要な値は以下です。
  ・REST API認証情報(APIトークン)

<REST API認証情報(APIトークン)>
REST APIでデータ連携を行うために必要な認証情報です。
詳細についてはAPI連携についてを確認してください。
また認証方法は「ユーザー認証」と「APIトークン認証」に2通りありますが、今回の例では「APIトークン認証」を利用します。

Javascriptの作成
実際に作成するJavascriptファイルです。
APIトークンなどは事前に確認した値を設定します
。 

js/sample01.js
/*!
* eSM プラグインサンプル04 ver 1.0
* 案件登録画面 商品情報項目制御スクリプト
* https://www.softbrain.co.jp/ 
* Copyright (c) SOFTBRAIN Co.,Ltd.
*/ 


// APIトークン 
var token = '968dea36-1137-42d3-bb81-cdeb0a806103'; 
// eSMのコンテキストパスを取得 
var contextPath = window.location.protocol + '//' + window.location.host + '/' + document.URL.split('/')[3];
// リクエストRestAPIのパス
var restApiUrl = '/rest/v1/entities/search';
// 商品入力欄のデータ接頭辞
var prefix = 'EXT_8_ANKEN_PRODUCT_VARCHAR_';
// 検索タイプ部署
var typeDep = 'dep';
// 部署ラベル
var labelDep = '部署名';
// 検索タイプ担当者
var typePer = 'per';
// 担当者ラベル 
var labelPer = '担当者';
// 検索テキストボックスID
var searchText = 'SearchText';
// 検索ボタンID
var viewResult = 'ViewResult';
// 検索候補リストID
var suggestList = 'SuggestList';
// 検索候補内容ID 
var tipList = 'TipList'
// 検索候補ツールチップID
var toolTip = 'ToolTip'

//-----共通関数-----

/** 
 * テキストコンポーネントを読み取り専用に変更
 * @param obj テーブルオブジェクト(td)
 * @param type インデックス(商品情報項目のデータ番号)
 * @param ankenProduct プロダクト番号
 */ 

function setReadOnly(obj, index, ankenProduct) {
    // コンポーネント名の構築
    var name = prefix + index + '_' + ankenProduct;

    if($('input:text[name="' + name + '"]').size()) {
        $('input:text[name="' + name + '"]').attr('readonly',true);
    }
}

/** 
 * 検索コンポーネントを追加
 * @param obj テーブルオブジェクト(td)
 * @param type データタイプ
 * @param ankenProduct プロダクト番号
 */ 

function appendSearchUI(obj, type, ankenProduct) { 

    var spanText = ''; 
    if(type == typeDep) {
        spanText = labelDep;
    } else {
        spanText = labelPer;
    }

    var searchPrefix = type + ankenProduct;
    if(obj != null) { 
        // 外枠
        var html = '<div class="searchArea addEmpSearchBox" style="margin-left: 0px; float: right;">';

        // 検索エリア
        html += '<div class="search" style="text-align: left; border-radius: 15px; box-shadow: 1px 1px 1 1 rgba(0, 0, 0, 0.4)">';
        html += '<span class="proPlaceholder" style="color: rgb(170, 170, 170); display: inline;" onclick="javascript: $(\'#' +searchPrefix + searchText + '\').focus();">' + spanText + '</span>';
        html += '<input id="' + searchPrefix + searchText + '" type="text" style="top: 1px;" autocomplete="off">';
        if(type == typeDep) {
            html += '<input id="' + searchPrefix + viewResult + '" type="button" onclick="viewDepartmentSuggestList($(\'#' + searchPrefix + searchText + '\').val(),\'' + ankenProduct + '\');" style="cursor: pointer;">';
        } else {
            html += '<input id="' + searchPrefix + viewResult + '" type="button" onclick="viewPersonSuggestList($(\'#' + searchPrefix + searchText + '\').val(),\'' + ankenProduct + '\');" style="cursor: pointer;">';
        } 
        html += '</div>';

        // 選択候補 
        html += '<div id="' + searchPrefix + suggestList + '" style="width: 250px; padding-top: 0px; display: none; -ms-overflow-x: hidden; max-height: none;">'; 
        html += '<ul id="' + searchPrefix + tipList + '" class="tipList"></ul>'; 
        html += '</div>';
        html += '</div>';

        // 外枠閉じ
        $(obj).append(html);
 
        // KeyUpイベントリスナー設定 
        $('#' + searchPrefix + searchText).keyup(function(e) {
            var charCode = (typeof e.which === "number") ? e.which : e.keyCode;
            // Enterキー入力
            if (charCode==13){
                // 検索ワードの取得
                var searchWord = $('#' + searchPrefix + searchText).val();
                // 候補リストの表示
                if(type == typeDep) {
                    viewDepartmentSuggestList(searchWord, ankenProduct);
                } else {
                    viewPersonSuggestList(searchWord, ankenProduct);
                }
            }
        }); 
    }
}

/**
 * 検索候補リスト作成
 * @param values 検索候補データ
 * @param type データタイプ 
 * @param ankenProduct プロダクト番号
 */ 

function createSuggestList(values, type, ankenProduct) {
    var searchPrefix = type + ankenProduct; 

    // 候補リストを空にする
    $('#' + searchPrefix + tipList).empty();

    // 候補リストにデータを追加する
    $(values).each(function(i, o){
        var name = o.item_data[0].text;
        var num = o.item_data[1].text;
        var html = null;
        if(type == typeDep) {
            html = '<li class="skc-res-item " onclick="setDepartmentSelectValue(\'' + ankenProduct + '\',\'' + name + '\',\'' + num + '\');" selectable="true"><div class="suggestedrow"><a href="javascript:void(0);"><span>' + name + '</span></a></div></li>';
        } else {
            html = '<li class="skc-res-item " onclick="setPersonSelectValue(\'' + ankenProduct + '\',\'' + name + '\',\'' + num + '\');" selectable="true"><div class="suggestedrow"><a href="javascript:void(0);"><span>' + name + '</span></a></div></li>';
        } 

        $('#' + searchPrefix + tipList).append(html);
    });

    // ツールチップの表示
    eSMQTips2.addTips(event, {'id':searchPrefix + toolTip,'my':'top center','at':'bottom center','scroll':false,'width':'240px','target':'#' + searchPrefix + searchText,'classes':'qtip-content-listview','dispData':'$(\'#' + searchPrefix + suggestList + '\')','events':{'visible':resizeTipFunc}});


//-----部署専用関数----- 

/**
 * 部署の検索候補リスト表示
 * @param searchWord 検索ワード
 * @param ankenProduct プロダクト番号
 */

function viewDepartmentSuggestList(searchWord, ankenProduct) {
    searchDepartment(searchWord, ankenProduct); 
}

/** 
 * 部署の選択設定
 * @param ankenProduct プロダクト番号
 * @param name 設定する部署名
 * @param num 設定する部署番号
 */

function setDepartmentSelectValue(ankenProduct, name, num) {
    var searchPrefix = typeDep + ankenProduct;

    // 部署番号
    $('input:text[name="' + prefix + '1_' + ankenProduct + '"]').val(num);
    // 部署名 
    $('input:text[name="' + prefix + '2_' + ankenProduct + '"]').val(name);
    // ツールチップを消す
    try{ $("#qtip-" + searchPrefix + toolTip).qtip('destroy', true); }catch(e){};


//-----担当者専用関数----- 

/**
 * 担当者の検索候補リスト表示
 * @param searchWord 検索ワード
 * @param ankenProduct プロダクト番号
 */ 

function viewPersonSuggestList(searchWord, ankenProduct) { 
    searchPerson(searchWord, ankenProduct);


/** 
 * 担当者の選択設定
 * @param ankenProduct プロダクト番号
 * @param name 設定する部署名
 * @param num 設定する部署番号
 */

function setPersonSelectValue(ankenProduct, name, num) {
    var searchPrefix = typePer + ankenProduct;

    // 担当者番号
    $('input:text[name="' + prefix + '3_' + ankenProduct + '"]').val(num);
    // 担当者名 
    $('input:text[name="' + prefix + '4_' + ankenProduct + '"]').val(name);
    // ツールチップを消す
    try{ $("#qtip-" + searchPrefix + toolTip).qtip('destroy', true); }catch(e){};
}

//-----ロードイベント関数----- 

/** 
 * ページロード時のイベント処理
 * 
 * 案件登録画面表示時に商品情報項目が存在した場合に
 * 部署、担当者の制御テーブルを追加する
 */ 

$(document).ready(function(){
    // 商品テーブルを検索
    //var tables = $("div").find('iframe').contents().find('.productTable');
    var tables = $('.productTable');
    if(tables.size()) {
        tables.each(function(i, o){
            // 商品コードを取得
            var productCode = $(o).closest('.commodityDetail').first().attr("data-product-code");

            // 部署番号
            var depNum = $(o).find('.dd')[10];
            setReadOnly(depNum, 1, productCode, "test dep num");
            // 部署名
            var depName = $(o).find('.dd')[11];
            setReadOnly(depName, 2, productCode, "test dep name"); 
            appendSearchUI(depName, typeDep, productCode);
            // 担当者番号
            var perNum = $(o).find('.dd')[12];
            setReadOnly(perNum, 3, productCode, "test per num");
            // 担当者名
            var perName = $(o).find('.dd')[13];
            setReadOnly(perName, 4, productCode, "test per name"); 
            appendSearchUI(perName, typePer, productCode);
        }); 
    }
});

//-----Ajax実行関数-----

/** 
 * 部署検索
 * @param searchWord 検索ワード 
 * @param ankenProduct プロダクト番号
 */

function searchDepartment(searchWord, ankenProduct) {
 
    // 検索ワードが未入力の場合は実行しない
    if(searchWord == null || searchWord == '') {
        return;
    } 

    // RestAPIのURLの構築
    var urlStr = contextPath + restApiUrl; 

     // 送信データ 
    var data = {  
        searchCondition: {
            targetObjectName: "depart",
            items: [ 
                { 
                    columnCode: 1502,
                    operator: "partly", 
                    text: searchWord
                }
            ]
        },
        columnCodes: [1502, 1506], 
        sortKeys: null,
        fromIndex: 1
    }; 

    // RestAPIへリクエストを送信
    $.ajax({  
        url:urlStr, // RestAPIのURL
        type:'POST', // HTTPメソッド 
        headers: { // リクエストヘッダー
            'X-Auth-API-Token' : token
        },
        contentType: 'application/json',
        data:JSON.stringify(data),
        dataType:'json', // 受信データタイプ
        cache: false, // キャッシュを無効
        timespan:1000 // タイムアウト時間(msec)
    }).done(function(data){
        if(data.values.length) { 
            createSuggestList(data.values, typeDep, ankenProduct);
        } 
    }).fail(function(XMLHttpRequest, textStatus, errorThrown){ 
        // 問題が発生した場合は警告ダイアログ表示
        alert(errorThrown);
    });


/** 
 * 担当者検索
 * @param searchWord 検索ワード
 * @param ankenProduct プロダクト番号
 */ 

function searchPerson(searchWord, ankenProduct) { 

    // 検索ワードが未入力の場合は実行しない 
    if(searchWord == null || searchWord == '') { 
        return;
    }

    // RestAPIのURLの構築
    var urlStr = contextPath + restApiUrl;

     // 送信データ
    var data = { 
        searchCondition: {
            targetObjectName: "employee",
            items: [
                { 
                    columnCode: 502,
                    operator: "partly",
                    text: searchWord
                } 
            ] 
        }, 
        columnCodes: [502, 602], 
        sortKeys: null, 
        fromIndex: 1
    };

    // RestAPIへリクエストを送信 
    $.ajax({ 
        url:urlStr, // RestAPIのURL 
        type:'POST', // HTTPメソッド 
        headers: { // リクエストヘッダー
            'X-Auth-API-Token' : token 
        },  
        contentType: 'application/json',
        data:JSON.stringify(data),
        dataType:'json', // 受信データタイプ
        cache: false, // キャッシュを無効
        timespan:1000 // タイムアウト時間(msec)
    }).done(function(data){ 
        if(data.values.length) {
            createSuggestList(data.values, typePer, ankenProduct);
        }  
    }).fail(function(XMLHttpRequest, textStatus, errorThrown){  
        // 問題が発生した場合は警告ダイアログ表示
        alert(errorThrown); 
    }); 
}  


 
作成が完了したらmanifest.jsonとともにzipファイルに圧縮し、eセールスマネージャーへアップロードを行います。
 

仕様書
今回のサンプルプログラムの仕様書を公開しています。
以下のリンクよりダウンロードしてください。