BookOfBusinessReachWidget = function( widgetOptions ) { this.dataGrid = null; this.rootElement = null; this.activeFilterPreferences = {}; this.dataGrid = null; this.rootElement = null; this.activeFilterPreferences = {}; this.widgetOptions = widgetOptions; this.widgetOptions.config = $.extend( true, {}, this.getDefaultConfig(), widgetOptions.config ); } BookOfBusinessReachWidget.prototype.constructor = BookOfBusinessReachWidget; BookOfBusinessReachWidget.prototype.getDefaultSize = function() { return { width : 3, height : 3 } } BookOfBusinessReachWidget.prototype.onDashboardLayout = function( layout ) { this.dataGrid.option( "height", layout.portletContentHeight - 15 ); } BookOfBusinessReachWidget.prototype.element = function() { if( this.rootElement ) return this.rootElement; let instance = this; instance.dataGrid = $("
").dxDataGrid( { showBorders : true, rowAlternationEnabled : true, scrolling : { mode : "virtual" }, columns : [ { dataField : "groupingName", caption : "Account Owner", calculateCellValue : function( rowData ) { return rowData.groupingName ? rowData.groupingName : "Unknown"; } }, {dataField : "bookOfBusinessAccounts", width : 100, dataType : "number", caption : "Total Accts."}, {dataField : "opportunityAccounts", width : 100, dataType : "number", caption : "With Opps", metricField : "withOpps"}, {dataField : "activeAccounts", width : 100, dataType : "number", caption : "Active", metricField : "active"}, {dataField : "activePlusAccounts", caption : "Active > 20%", width : 100, dataType : "number", metricField : "activePlus"}, {dataField : "saleAccounts", width : 100, dataType : "number", caption : "Sale", metricField : "sale"}, {dataField : "noSaleAccounts", width : 100, dataType : "number", caption : "No Sale", metricField : "noSale"}, {dataField : "closeRate", width : 100, dataType : "number", format : "percent", metricField : "sale"}, {dataField : "planReach", caption : "Reach", width : 100, dataType : "number", format : "percent", caption : "Reach", metricField : "withOpps" } ], summary : { totalItems : [ { column : "bookOfBusinessAccounts", summaryType : "sum", displayFormat : "{0}" }, { column : "opportunityAccounts", summaryType : "sum", displayFormat : "{0}" }, { column : "activeAccounts", summaryType : "sum", displayFormat : "{0}" }, { column : "activePlusAccounts", summaryType : "sum", displayFormat : "{0}" }, { column : "saleAccounts", summaryType : "sum", displayFormat : "{0}" }, { column : "noSaleAccounts", summaryType : "sum", displayFormat : "{0}" }, ] }, onCellClick : function( e ) { if( ! e.column.metricField || e.rowType != "data" ) { return; } instance.viewOpportunities( e.data, e.column.metricField ) }, onToolbarPreparing : function( e ) { if( ! e.toolbarOptions.items ) e.toolbarOptions.items = []; e.toolbarOptions.items.push( { location : "after", template : function() { let button = $("
").dxButton( { icon : "export", hint : "Excel Export", onClick : function( ee ) { Fse.UI.DataGridHelper.exportDataGrid( { dataGrid : e.component, fileName : "BookOfBusinessReach.xlsx" } ); } }).dxButton( "instance" ); $("
").dxContextMenu( { target : button.element(), items : [ { text : "Export Opportunities...", id : "exportOpportunities" } ], onItemClick : function( e ) { if( e.itemData.id == "exportOpportunities" ) { instance.exportOpportunityData(); } }, hideOnOutsideClick : true }).appendTo( $("body") ) return button.element(); }, xwidget : "dxButton", xoptions : { icon : "export", hint : "Excel Export", onClick : function( ee ) { Fse.UI.DataGridHelper.exportDataGrid( { dataGrid : e.component, fileName : "BookOfBusinessReach.xlsx" } ); } } }) } }).dxDataGrid( "instance" ); instance.rootElement = $("
").addClass( "BookOfBusinessReachWidget" ); instance.rootElement.append( instance.dataGrid.element() ) instance.applyFilters(); return instance.rootElement } BookOfBusinessReachWidget.prototype.exportOpportunityData = function() { let instance = this; let objectParams = $.extend( true, {}, instance.objectParams ) objectParams.returnType = "opportunityData"; let exportGrid = $("
").dxDataGrid( { visible : false, columns : [ { dataField : "opportunityId", caption : "Opportunity ID", dataType : "number", format : "fixedPoint" }, { dataField : "partnerTerritoryName", caption : "Account Territory" }, { dataField : "partnerName", caption : "Account Name" }, { dataField : "estOrderDate", dataType : "date" }, { dataField : "product", caption : "Product Code" }, { dataField : "shortDesc", caption : "Product Description" }, { dataField : "sku", caption : "SKU Code" }, { dataField : "skuDesc", caption : "SKU Description" }, { dataField : "cases", dataType : "number" }, { dataField : "lbs", dataType : "number" }, { dataField : "dollars", dataType : "number" }, { dataField : "effectiveRepEmail", dataField : "Sales Rep Email" }, { dataField : "stageName", caption : "Stage" }, { dataField : "likelihoodPercent", caption : "Likely %", format : "percent", dataType : "number" } ], dataSource : Fse.Data.newDataSource( { object : "SPL.bookOfBusinessReach", paginate : false, objectParams : objectParams }) }).dxDataGrid( "instance" ); exportGrid.getDataSource().load().done( function() { Fse.UI.DataGridHelper.exportDataGrid( { dataGrid : exportGrid, fileName : "OpportunityData.xlsx" } ); }) } BookOfBusinessReachWidget.prototype.viewOpportunities = function( data, metricField ) { let instance = this; instance.loadMetaData().done( function( metaData ) { console.log( `META DATA ${data.accountType}` ); console.log( metaData ); let searchParams = { effectiveRepOwnerType : "MFR", partnerClassificationKey : [] } if( metricField == "active" ) { searchParams.stageId = metaData.activeStageIds } else if ( metricField == "activePlus" ) { searchParams.stageId = metaData.activePlusStageIds } else if ( metricField == "sale" ) { searchParams.stageId = metaData.saleStageIds } else if ( metricField == "noSale" ) { searchParams.stageId = metaData.noSaleStageIds } else { searchParams.stageId = metaData.stageIds } let fieldNames = [ "bobOPRClassificationIds", "bobCDRClassificationIds" ]; fieldNames.forEach( function( fn ) { let partnerType = "OPR"; if( fn.search( /CDR/ ) > 0 ) { partnerType = "CDR"; } let classificationIds = metaData[fn]; classificationIds.forEach( function( classificationId ) { searchParams.partnerClassificationKey.push( `${partnerType}:${classificationId}` ); }) }) // merge in widget filters for( p in instance.widgetOptions.config ) { if( instance.widgetOptions.config[p] && ! searchParams[p]) { searchParams[p] = instance.widgetOptions.config[p] } } // translate salesRepId if( searchParams.salesRepId ) { searchParams.effectiveRepId = searchParams.salesRepId; } delete searchParams.salesRepId; if( ! searchParams.effectiveRepId ) { searchParams.excludeEffectiveRepId = metaData.excludeSalesRepId; } // translate territoryPath if( searchParams.territoryPath ) { searchParams.partnerTerritoryPath = searchParams.territoryPath; } delete searchParams.territoryPath; // remove the grouping search param from the filters delete searchParams.grouping; // change this to a readable value let groupings = new DevExpress.data.ArrayStore( { data : [ { value : "accountOwnerFullName", searchParam : "effectiveRepId" }, { value : "divisionName", searchParam : "partnerTerritoryPath" }, { value : "regionName", searchParam : "partnerTerritoryPath" }, { value : "territoryName", searchParam : "partnerTerritoryPath" }, { value : "segmentName", searchParam : "clientSegPath" } ], key : "value" }) let groupingSearchParam = null; groupings.byKey( instance.widgetOptions.config.grouping ).done( function( grouping ) { groupingSearchParam = grouping.searchParam; }) searchParams[groupingSearchParam] = data.groupingKey; console.log( "Click Search Params" ); console.log( searchParams ); instance.widgetOptions.dashboard.tabSearch( "OpportunityAnalyzer", searchParams ); }) } BookOfBusinessReachWidget.prototype.loadMetaData = function() { let instance = this; let d = $.Deferred(); let appDataURL = $("link#appDataURL").attr( "href" ); let bobMetaDataURL = Fse.Util.updateURL( appDataURL, { object : "SPL.bookOfBusinessMetaData", mode : "direct" } ) $.ajax( { url : bobMetaDataURL, method : "GET" }).done( function( metaData ) { instance.metaData = metaData; d.resolve( instance.metaData ); }).fail( function() { d.reject(); }) return d; } BookOfBusinessReachWidget.prototype.edit = function( applyFn ) { this.showGrouping = true; let widgetPreferenceEditor = new BookOfBusinessWidgetPreferences( this ); widgetPreferenceEditor.show( applyFn ); } BookOfBusinessReachWidget.prototype.getDefaultConfig = function() { let estOrderDateRange = Fse.CRM.dateRanges.rangeByKey( "F,TY" ); // fiscal this year return { territoryPath : null, productHierarchyPath : null, estOrderDateStart : estOrderDateRange.startDate, estOrderDateEnd : estOrderDateRange.endDate, estOrderDateRange : estOrderDateRange.rangeKey, clientSegPath : null, productSetTags : null, partnerType : null, salesRepId : null, objPath : null, grouping : "accountOwnerFullName" } } BookOfBusinessReachWidget.prototype.applyPreferences = function( preferences ) { this.widgetOptions.config = $.extend( true, {}, preferences ); this.applyFilters(); } BookOfBusinessReachWidget.prototype.applyFilters = function() { let instance = this; let widgetFilters = $.extend( true, {}, instance.widgetOptions.config ); let filterPreferences = instance.widgetOptions.filterPreferences; let activeFilterPreferences = instance.activeFilterPreferences; // global filters suppored by this widget, when the global filters are expressed with different variable names, they need to translated to this widgets variable names const globalFilters = { // widgetVariableName : "globalFilterVariableName" salesRepId : "salesRepId", territoryPath : "territoryPath", productHierarchyPath : "productHierarchyPath", productSetTags : "productSetTags", clientSegPath : "clientSegPath" }; for( let p in globalFilters ) { // apply translation - global filter param is not the same name as widget param let gp = globalFilters[p]; // if the global filter preferences have the propery and it has a value, update the objectparams if( filterPreferences.hasOwnProperty( gp ) && filterPreferences[gp]) { if( widgetFilters[p] == null ) { widgetFilters[p] = filterPreferences[gp]; activeFilterPreferences[p] = widgetFilters[p]; // indicate that this widget parameter has an active filter preference - used when displaying the edit page } } } let opportunityFilters = []; let accountFilters = []; let salesRepFilterActive = false; if( widgetFilters.estOrderDateStart && widgetFilters.estOrderDateEnd ) { opportunityFilters.push( [ [ "estOrderDate", ">=", widgetFilters.estOrderDateStart ], "and", [ "estOrderDate", "<=", widgetFilters.estOrderDateEnd ] ]) } if( widgetFilters.productSetTags ) { let filter = []; widgetFilters.productSetTags.forEach( function( ve ) { if( filter.length ) { filter.push( "or" ); } filter.push( [ "productSetTags", "contains", ve ] ); }) if( filter.length ) { if( opportunityFilters.length ) opportunityFilters.push( "and" ); opportunityFilters.push( filter ); } } if( widgetFilters.productHierarchyPath ) { let filter = []; widgetFilters.productHierarchyPath.forEach( function( ve ) { if( filter.length ) { filter.push( "or" ); } filter.push( [ "productHierarchyPath", "startswith", ve ] ); }) if( filter.length ) { if( opportunityFilters.length ) opportunityFilters.push( "and" ); opportunityFilters.push( filter ); } } if( widgetFilters.objPath ) { let objPaths = widgetFilters.objPath; // there are 5 objective path fields to search if( ! Array.isArray( objPaths ) ) { objPaths = [objPaths]; } filter = []; objPaths.forEach( function( objPath ) { let objPathFilter = [ ["objPath1", "startswith", objPath], "or", ["objPath2", "startswith", objPath], "or", ["objPath3", "startswith", objPath], "or", ["objPath4", "startswith", objPath], "or", ["objPath5", "startswith", objPath] ] if( filter.length ) { filter.push( "or" ) } filter.push( objPathFilter ); }) if( filter.length ) { if( opportunityFilters.length ) opportunityFilters.push( "and" ); opportunityFilters.push( filter ); } } if( widgetFilters.territoryPath ) { let filter = []; widgetFilters.territoryPath.forEach( function( ve ) { if( filter.length ) { filter.push( "or" ); } filter.push( [ "territoryPath", "startswith", ve ] ); }) if( filter.length ) { if( accountFilters.length ) accountFilters.push( "and" ); accountFilters.push( filter ); } } if( widgetFilters.clientSegPath ) { let filter = []; widgetFilters.clientSegPath.forEach( function( ve ) { if( filter.length ) { filter.push( "or" ); } filter.push( [ "clientSegPath", "startswith", ve ] ); }) if( filter.length ) { if( accountFilters.length ) accountFilters.push( "and" ); accountFilters.push( filter ); } } if( widgetFilters.salesRepId ) { let filter = []; widgetFilters.salesRepId.forEach( function( ve ) { if( filter.length ) { filter.push( "or" ); } filter.push( [ "salesRepId", "=", ve ] ); }) if( filter.length ) { if( accountFilters.length ) accountFilters.push( "and" ); accountFilters.push( filter ); salesRepFilterActive = true; } } if( widgetFilters.partnerType ) { if( accountFilters.length ) accountFilters.push( "and" ); accountFilters.push( [ "partnerType", "=", widgetFilters.partnerType ] ); } objectParams = { salesRepFilterActive : salesRepFilterActive, grouping : widgetFilters.grouping }; if( opportunityFilters.length ) { objectParams.opportunityFilters = opportunityFilters; } if( accountFilters.length ) { objectParams.accountFilters = accountFilters; } let groupingNameCaption = widgetFilters.grouping; // change this to a readable value let groupings = new DevExpress.data.ArrayStore( { data : [ { value : "accountOwnerFullName", caption : "Account Owner" }, { value : "divisionName", caption : "Division" }, { value : "regionName", caption : "Region" }, { value : "territoryName", caption : "Territory" }, { value : "segmentName", caption : "Segment" } ], key : "value" }) groupings.byKey( widgetFilters.grouping ).done( function( grouping ) { groupingNameCaption = grouping.caption; }) instance.objectParams = objectParams; instance.dataGrid.option( "dataSource", Fse.Data.newDataSource( { object : "SPL.bookOfBusinessReach", paginate : false, objectParams : objectParams }) ); instance.dataGrid.columnOption( "groupingName", { caption : groupingNameCaption } ) } Fse.Portal.addWidgetFactory( "BookOfBusinessReachWidget", function( widgetDef ) { return new BookOfBusinessReachWidget( widgetDef ); })