SalesCallPerformanceWidget = function( widgetOptions ) { let configDefaults = this.getDefaultConfig(); this.widgetOptions = widgetOptions; this.widgetOptions.config = $.extend( true, {}, configDefaults, this.widgetOptions.config ); // keeps track of the active filterPreferences this.activeFilterPreferences = {}; this.readyPromise = $.Deferred(); this.rootElement = null; this.dataGrid = null; this.init(); } SalesCallPerformanceWidget.prototype.constructor = SalesCallPerformanceWidget; SalesCallPerformanceWidget.prototype.element = function() { return this.rootElement; } SalesCallPerformanceWidget.prototype.ready = function() { return this.readyPromise; } SalesCallPerformanceWidget.prototype.getDefaultSize = function() { return { width : 2, height : 2 } } SalesCallPerformanceWidget.prototype.getDefaultConfig = function() { let interactionDateRange = Fse.CRM.dateRanges.rangeByKey( "F,TY" ); //This Year; if( Fse.Portal.appConfiguration.STP.ownerType === "BRO" ) { interactionDateRange = Fse.CRM.dateRanges.rangeByKey( "N,TY" ); } const defaults = { salesRepId : null, salesRepOwnerType : null, excludeDeletedSalesReps : false, clientSegPath : null, territoryPath : null, interactionDateStart : interactionDateRange.startDate, interactionDateEnd : interactionDateRange.endDate, interactionDateRange : interactionDateRange.rangeKey, partnerType : null, groupType : "salesRep" }; if( Fse.Portal.appConfiguration.STP.ownerType === "BRO" ) { defaults.mfr_id = null; } return defaults; } SalesCallPerformanceWidget.prototype.init = function() { let instance = this; this.createUI(); let configForDataSource = $.extend( true, {}, instance.widgetOptions.config ); const globalFilters = { territoryPath : "territoryPath", salesRepId : "salesRepId", salesRepOwnerType : "staffType", mfr_id : "mfr_id", clientSegPath : "clientSegPath" }; for( let p in globalFilters ) { let gp = globalFilters[p]; if( instance.widgetOptions.filterPreferences.hasOwnProperty( gp ) && instance.widgetOptions.filterPreferences[gp] ) { if( configForDataSource[p] == null ) { configForDataSource[p] = instance.widgetOptions.filterPreferences[gp]; instance.activeFilterPreferences[p] = configForDataSource[p]; // indicate that this widget parameter has an active filter preference - used when displaying the edit page } } } let objectParams = {} let configFields = [ "groupType", "territoryPath","salesRepId","interactionDateStart","interactionDateEnd","mfr_id","partnerType","salesRepOwnerType", "excludeDeletedSalesReps","clientSegPath", "interactionMethod"]; configFields.forEach( function( dataField ) { if( configForDataSource[dataField] ) { objectParams[dataField] = configForDataSource[dataField]; } }) let ds = Fse.Data.newDataSource( { object : "CRM.interactionSummary", paginate : false, objectParams : objectParams } ); ds.load().done( function( data ) { instance.readyPromise.resolve(); instance.dataGrid.option( "dataSource", data ); }) } SalesCallPerformanceWidget.prototype.createUI = function() { let instance = this; instance.rootElement = $("
") instance.dataGrid = instance.createDataGrid(); instance.rootElement.append( instance.dataGrid.element() ); return instance.rootElement; } SalesCallPerformanceWidget.prototype.createDataGrid = function() { let instance = this; instance.groupTypesStore = new DevExpress.data.ArrayStore( { data : [ { text : "Sales Rep", groupType : "salesRep" }, { text : "Territory", groupType : "territory" }, { text : "Region", groupType : "region" }], key : "groupType" }); let groupCaption = "Group"; instance.groupTypesStore.byKey( instance.widgetOptions.config.groupType ).done( function( data ) { groupCaption = data.text; }) let headerCellTemplate = function( container, options ) { if( options.column.hint ) { container.attr( "title", options.column.hint ); } container.append( options.column.caption ) } return $("
").dxDataGrid( { scrolling : { mode : "virtual" }, showBorders : true, height : "100%", columns : [ { dataField : "groupDisplay", caption : groupCaption, width : "auto", calculateDisplayValue : function( data ) { if( ! data || ! data.groupDisplay ) return; if( ! data.groupColumn.match( /Path$/ )) { return data.groupDisplay; } let parts = data.groupDisplay.split( "/" ); if( parts.length == 1 ) { return data.groupDisplay; } else { let lastPart = parts[parts.length-1].replace( "|", "/" ); return `.../${lastPart}`; } }, cellTemplate : function( container, options ) { if( options.rowType != "data" ) return; if( options.data.groupColumn.match( /Path$/) ) { container.attr( { "title" : options.value } ) } container.append( options.displayValue ); } }, // call fields { dataField : "totalCalls", caption : "Calls", hint : "Total Calls", xwidth :50, dataType : "number", format : { type : "fixedPoint", precision : 0 }, headerCellTemplate : headerCellTemplate }, { dataField : "callsPerWeek", caption : "Calls/Wk", hint : "Average Calls per Week", xwidth : 80, dataType : "number",format : { type : "fixedPoint", precision : 1 }, headerCellTemplate : headerCellTemplate }, { dataField : "callsPerWeekPerRep", caption : "Rep/Calls/Wk", hint : "Average Calls per Rep per Week", width : 100, dataType : "number", format : { type : "fixedPoint", precision : 1 }, headerCellTemplate : headerCellTemplate }, { dataField : "uniqueAccountsPct", caption : "Uniq Accts", hint: "% of Unique Accounts Called On", xwidth : 80, dataType : "number", format : { type : "percent", precision : 1 }, headerCellTemplate : headerCellTemplate}, // debug { visible : false, dataField : "uniqueAccounts", caption : "UA", width : 50, dataType : "number", format : { type : "fixedPoint", precision : 0 }, headerCellTemplate : headerCellTemplate }, { visible : false, dataField : "newAccounts", caption : "NA", width : 50, dataType : "number", format : { type : "fixedPoint", precision : 0 }, headerCellTemplate : headerCellTemplate }, { dataField : "newAccountsPct", caption : "New Accts", hint: "% of New Accounts Called", xwidth: 80, dataType : "number", format : { type : "percent", precision : 1 }, headerCellTemplate : headerCellTemplate }, { dataField : "objectiveCallsPct", caption : "Obj Calls", hint: "% of Calls Tied to Objectives", xwidth : 80, dataType : "number", format : { type : "percent", precision : 1 }, headerCellTemplate : headerCellTemplate }, // item fields { dataField : "totalPresentations", caption : "Items", hint: "Total Number of Items Presented", xwidth : 55, dataType : "number", format : { type : "fixedPoint", precision : 0 }, headerCellTemplate : headerCellTemplate }, // debug { visible : false, dataField : "totalWins", caption : "Wins", width : 50, dataType : "number", format : { type : "fixedPoint", precision : 0 }, headerCellTemplate : headerCellTemplate }, { visible : false, dataField : "totalLoses", caption : "Losses", width : 50, dataType : "number", format : { type : "fixedPoint", precision : 0 }, headerCellTemplate : headerCellTemplate }, { visible : false, dataField : "totalFollowUps", caption : "FUs", width : 50, dataType : "number", format : { type : "fixedPoint", precision : 0 }, headerCellTemplate : headerCellTemplate }, { dataField : "closeRate", caption : "Close", hint: "Close Rate % (Sale / Sale + No Sale)", xwidth : 55, dataType : "number", format : { type : "percent", precision : 1 }, headerCellTemplate : headerCellTemplate }, { dataField : "efficiencyRate", caption : "Eff", hint : "Efficiency % (Sale / Sale + Follow Up + No Sale)", xwidth : 55, dataType : "number", format : { type : "percent", precision : 1 }, headerCellTemplate : headerCellTemplate }, { dataField : "avgCallsToClose", caption : "Calls/Close", hint :"Average Number of Calls to Close", xwidth : 75, dataType : "number", format : { type : "fixedPoint", precision : 1 }, headerCellTemplate : headerCellTemplate }, { dataField : "avgItemsPerCall", caption : "Items/Call", hint : "Average Number of Items per Call", xwidth : 75, dataType : "number", format : { type : "fixedPoint", precision : 1 }, headerCellTemplate : headerCellTemplate } ], onCellPrepared: function(e) { if (e.rowType === "data") { if (e.column.dataField === "newAccountsPct" && e.data.newAccounts ) { e.cellElement.attr( { "title" : `${e.data.newAccounts} New Accounts` } ); } if (e.column.dataField === "uniqueAccountsPct" && e.data.uniqueAccounts ) { e.cellElement.attr( { "title" : `${e.data.uniqueAccounts} Unique Accounts` } ); } } }, onCellClick : function( e ) { if( e.rowType != "data" ) return; instance.viewInteractions( e.data ); }, onToolbarPreparing : function( e ) { if( ! e.toolbarOptions.items ) { e.toolbarOptions.items = []; } let dataGrid = e.component; e.toolbarOptions.items.push( { location: "after", widget : "dxButton", options : { hint : "Excel Export", icon : "export", onClick : function( e ) { Fse.UI.DataGridHelper.exportDataGrid( { dataGrid : dataGrid, fileName : "salescallperformance.xlsx" } ); }}}); } }).dxDataGrid("instance"); } SalesCallPerformanceWidget.prototype.viewInteractions = function( data ) { let instance = this; let itemParams = instance.widgetOptions.config; let dashboard = instance.widgetOptions.dashboard; let anlayzerSearchReady = $.Deferred(); let analyzerParams = { disposition : 'X', interactionDateStart : itemParams.interactionDateStart, interactionDateEnd : itemParams.interactionDateEnd, interactionDateRange : itemParams.interactionDateRange, excludeDirectMarketing : true }; if( itemParams.territoryPath ) { analyzerParams.territoryPath = itemParams.territoryPath; } if( itemParams.salesRepId ) { analyzerParams.salesRepId = itemParams.salesRepId; } if( itemParams.partnerType ) { analyzerParams.partnerType = itemParams.partnerType; } if( itemParams.mfr_id ) { analyzerParams.mfr_id = itemParams.mfr_id; } if( itemParams.salesRepOwnerType ) { analyzerParams.salesRepOwnerType = itemParams.salesRepOwnerType } if( itemParams.excludeDeletedSalesReps ) { analyzerParams.excludeDeletedSalesReps = true } if( itemParams.clientSegPath ) { analyzerParams.clientSegPath = itemParams.clientSegPath } if( itemParams.interactionMethod ) { analyzerParams.interactionMethod = itemsParams.interactionMethod; } if( itemParams.groupType == "salesRep" ) { console.log( data ); analyzerParams.salesRepId = data.groupId; anlayzerSearchReady.resolve(); } else if ( itemParams.groupType == "territory" ) { // get the territory path for a territoryId (groupId) let territoryStore = Fse.Data.newDataSource( { object : "TER.salesTerritories", key : "TerritoryId" } ).store(); territoryStore.byKey( data.groupId ).done( function( territoryData ) { analyzerParams.territoryPath = territoryData.territoryPath; anlayzerSearchReady.resolve(); }) } else if ( itemParams.groupType == "region" ) { // get the territory path for a territoryName (groupId ) let regionDataSource = Fse.Data.newDataSource( { object : "TER.salesTerritories", key : "TerritoryId", paginate : false, filter : [ "territoryName", "=", data.groupId ] } ) regionDataSource.load().done( function( regions ) { if( regions.length ) { analyzerParams.territoryPath = regions[0].territoryPath; anlayzerSearchReady.resolve(); } }) } else { anlayzerSearchReady.resolve(); } anlayzerSearchReady.done( function() { dashboard.tabSearch( "InteractionAnalyzer", analyzerParams ) }) } SalesCallPerformanceWidget.prototype.edit = function( applyFn ) { let instance = this; if( ! instance.preferencesPopup ) { const items = []; items.push( { label : { location : "left", text : "Territory" }, dataField : "territoryPath", editorType : "dxDropDownBox", cssClass : instance.activeFilterPreferences.territoryPath ? 'fx-active-preference' : null, // scrum 46030 editorOptions : Fse.UI.multiSelectDropDownBoxEditorOptions( { dataSource : Fse.Data.newDataSource( { dataURL : instance.dataURL, object : "TER.salesTerritories", keyField : "territoryPath" } ), searchExpr : "territoryPath", searchMode : "contains", displayExpr : "territoryPath", multipleSelectedDisplay : "Multiple Territories Selected", keyExpr : "territoryPath", title : "Select Territories" }) }); items.push( { label : { text : "Sales Rep", location : "left" }, dataField : "salesRepId", editorType : "dxDropDownBox", cssClass : instance.activeFilterPreferences.effectiveRepId ? 'fx-active-preference' : null, // scrum 46030 editorOptions : Fse.UI.multiSelectDropDownBoxEditorOptions( { dataSource : Fse.Data.newDataSource( { dataURL : instance.dataURL, object : "CRM.salesRepList", keyField : "valueId", objectParams : { version : 2 } } ), searchExpr : "valueName", searchMode : "contains", displayExpr : "valueName", multipleSelectedDisplay : "Multiple Sales Reps Selected", keyExpr : "valueId", title : "Select Sales Reps" }) }); if( Fse.Portal.appConfiguration.STP.ownerType === "MFR" ) { let staffTypeList = [ { "text": "Manufacturer Staff Only" , "staffType": "MFR"}, { "text": "Broker Staff Only" ,"staffType": "BRO"} ]; items.push( { label : { location : "left", text : "Staff Type" }, dataField : "salesRepOwnerType", editorType : "dxSelectBox", cssClass : instance.activeFilterPreferences.salesRepOwnerType ? 'fx-active-preference' : null, editorOptions : { dataSource: { store : { type : "array", data : staffTypeList, key : "staffType" }}, displayExpr: "text", valueExpr : "staffType", placeholder : "Select Staff Type", showClearButton: true } }) } items.push( { dataField : "excludeDeletedSalesReps", label : { text : "Exclude Inactive & Deleted Sales Reps" }, editorType : "dxSwitch" }) items.push( { label : { location : "left", text : "Call Date" }, dataField : "interactionDateRange", editorType : "dxDropDownBox", editorOptions : Fse.UI.dateRangePickerOptions( { applyCustomDateRange : function( startDate, endDate ) { instance.preferencesForm.updateData( { interactionDateStart : startDate, interactionDateEnd : endDate } ) } } ) } ) if( Fse.Portal.appConfiguration.STP.ownerType === "BRO" ) { items.push( { label : { location : "left", text : "Manufacturer" }, dataField : "mfr_id", editorType : "dxDropDownBox", cssClass : instance.activeFilterPreferences.mfr_id ? 'fx-active-preference' : null, // scrum 46030 editorOptions : Fse.UI.multiSelectDropDownBoxEditorOptions( { dataSource : Fse.Data.newDataSource( { dataURL : instance.dataURL, object : "PRD.manufacturers", keyField : "mfr_id" } ), searchExpr : "mfr_name", searchMode : "contains", displayExpr : "mfr_name", multipleSelectedDisplay : "Multiple Manufacturers Selected", keyExpr : "mfr_id", title : "Select Manufacturer" }) }) } items.push( { label : { text : "Customer Type" }, dataField : "partnerType", editorType : "dxSelectBox", editorOptions : { showClearButton : true, dataSource : { store : { type : "array", data : [ { text : "Operators", partnerType : "OPR"}, { text : "Distributors", partnerType : "CDR" } ], key : "partnerType" } }, displayExpr : "text", valueExpr : "partnerType", placeholder : "Select Customer Type" } }) items.push( { dataField : "groupType", label : { text : "Group by" }, editorType : "dxSelectBox", editorOptions : { dataSource : { store : instance.groupTypesStore }, displayExpr : "text", valueExpr : "groupType" } }); items.push({ dataField : "clientSegPath", label : { location : "left", text : "Segment" }, editorType : "dxDropDownBox", cssClass : instance.activeFilterPreferences.clientSegPath ? 'fx-active-preference' : null, editorOptions : Fse.UI.multiSelectDropDownBoxEditorOptions( { dataSource : Fse.Data.newDataSource( { dataURL : instance.dataURL, object : "SPL.segments", keyField : "clientSegPath" } ), searchExpr : "clientSegPath", searchMode : "contains", displayExpr : "clientSegPath", multipleSelectedDisplay : "Multiple Segments Selected", keyExpr : "clientSegPath", title : "Select Segments" }) }); items.push( { label : { location : "left", text : "Call Method" }, dataField : "interactionMethod", editorType : "dxDropDownBox", editorOptions : Fse.UI.multiSelectDropDownBoxEditorOptions( { dataSource : Fse.Data.newDataSource( { object : "CRM.interactionMethods", key : "methodCode" } ), searchExpr : "methodName", displayExpr : "methodName", searchMode : "contains", multipleSelectedDisplay : "Multiple Methods Selected", keyExpr : "methodCode", title : "Select Method" }) }) if( Object.keys( instance.activeFilterPreferences ).length ) { items.push( { template : function() { let annotation = $("
").append( $("").addClass( "dx-field-item-label-text" ).append( "*filter preference active")).addClass( "fx-active-preference" ).css( { "cursor" : "pointer" } ); annotation.on( "click", function( e ) { // if the global filter preferences are applied, then close this popup let onSaveFilterPreferences = function() { instance.preferencesPopup.hide(); } instance.widgetOptions.dashboard.editFilterPreferences( onSaveFilterPreferences ); }) return annotation; } }) } let formData = { territoryPath : instance.widgetOptions.config.territoryPath, salesRepId : instance.widgetOptions.config.salesRepId, mfr_id : instance.widgetOptions.config.mfr_id, interactionDateStart : instance.widgetOptions.config.interactionDateStart, insteractionDateEnd : instance.widgetOptions.config.interactionDateEnd, interactionDateRange : instance.widgetOptions.config.interactionDateRange, partnerType : instance.widgetOptions.config.partnerType, groupType : instance.widgetOptions.config.groupType, salesRepOwnerType : instance.widgetOptions.config.salesRepOwnerType, excludeDeletedSalesReps : instance.widgetOptions.config.excludeDeletedSalesReps, clientSegPath : instance.widgetOptions.config.clientSegPath, interactionMethod : instance.widgetOptions.config.interactionMethod }; instance.preferencesPopup = $("
").dxPopup( { title : "Preferences", width : 450, height : "auto", position : { my : "right top", at : "right top", of : instance.widgetOptions.portlet }, hideOnOutsideClick : true, contentTemplate : function() { instance.preferencesForm = $("
").dxForm( { colCount : 1, items : items, } ).dxForm("instance"); instance.preferencesForm.updateData( formData ); return instance.preferencesForm.element(); }, toolbarItems : [ { // scrum 49810.14 toolbar : "bottom", location : "after", widget : "dxButton", options : { text : "Use Defaults", type : "normal", onClick : function() { instance.preferencesPopup.hide(); applyFn( instance.getDefaultConfig() ); } } }, { toolbar : "bottom", location : "after", widget : "dxButton", options : { text : "Apply", type : "default", onClick : function() { instance.preferencesPopup.hide(); applyFn( instance.preferencesForm.option( "formData" ) ); } } } ] } ).dxPopup( "instance" ); instance.preferencesPopup.element().appendTo( "body" ); } instance.preferencesPopup.show(); } Fse.Portal.addWidgetFactory( "SalesCallPerformanceWidget", function( widgetDef ) { return new SalesCallPerformanceWidget( widgetDef ); })