DecliningOperatorPurchasingWidget = function( widgetOptions ) { this.widgetOptions = widgetOptions; let configDefaults = this.getDefaultConfig(); this.widgetOptions.config = $.extend( true, {}, configDefaults, this.widgetOptions.config ); // keeps track of the active filterPreferences this.activeFilterPreferences = {}; // scrum 46030 this.dataGrid = null; this.preferencesPopup = null; this.readyPromise = $.Deferred(); this.init(); }; DecliningOperatorPurchasingWidget.prototype.element = function() { return this.dataGrid.element(); }; DecliningOperatorPurchasingWidget.prototype.getDefaultConfig = function( ) { return { viewBy : "operator", declinePctThreshold : 50, uom : "quantity" } } DecliningOperatorPurchasingWidget.prototype.getDefaultSize = function() { return { width : 2, height : 2 } } DecliningOperatorPurchasingWidget.prototype.init = function() { let instance = this; // let configForDataSource = $.extend( true, {}, instance.widgetOptions.config ); let objectParams = { viewBy : instance.widgetOptions.config.viewBy, uom : instance.widgetOptions.config.uom, declinePctThreshold : instance.widgetOptions.config.declinePctThreshold, clientSegPath : null, territoryPath : null, accountOwnerUserId : null, productHierarchyPath : null, mfr_id : null, lytdVolumeThreshold : null } if( instance.widgetOptions.config.clientSegPath ) { objectParams.clientSegPath = instance.widgetOptions.config.clientSegPath; } if( instance.widgetOptions.config.territoryPath ) { objectParams.territoryPath = instance.widgetOptions.config.territoryPath; } if( instance.widgetOptions.config.accountOwnerUserId ) { objectParams.accountOwnerUserId = instance.widgetOptions.config.accountOwnerUserId; } if( instance.widgetOptions.config.mfr_id ) { objectParams.mfr_id = instance.widgetOptions.config.mfr_id; } if( instance.widgetOptions.config.productHierarchyPath ) { objectParams.productHierarchyPath = instance.widgetOptions.config.productHierarchyPath; } if( instance.widgetOptions.config.lytdVolumeThreshold ) { objectParams.lytdVolumeThreshold = instance.widgetOptions.config.lytdVolumeThreshold; } if( instance.widgetOptions.config.declinePctThreshold ) { objectParams.declinePctThreshold = instance.widgetOptions.config.declinePctThreshold; } // when the global filters are expressed with different variable names, they need to translated to this widgets variable names const globalFilterTranslation = { // widgetVariableName : "globalFilterVariableName" clientSegPath : "clientSegPath", accountOwnerUserId : "salesRepId", territoryPath : "territoryPath", mfr_id : "mfr_id" } for( let p in objectParams ) { let gp = p; // global filter param is the same name as widget param // apply translation - global filter param is not the same name as widget param if( globalFilterTranslation[p] ) { gp = globalFilterTranslation[p]; } // if the global filter preferences have the propery and it has a value, update the objectparams if( instance.widgetOptions.filterPreferences.hasOwnProperty( gp ) && instance.widgetOptions.filterPreferences[gp]) { if( objectParams[p] == null ) { objectParams[p] = instance.widgetOptions.filterPreferences[gp]; instance.activeFilterPreferences[p] = objectParams[p]; // indicate that this widget parameter has an active filter preference - used when displaying the edit page } } } instance.dataSourceObjectParams = objectParams; let dataSource = Fse.Data.newDataSource( { object : "OPA.decliningPurchasing", paginate : false, objectParams : objectParams } ); dataSource.on( "loadingChanged", function( isLoading ) { if( ! isLoading ) { instance.readyPromise.resolve(); } } ); instance.dataGrid = instance.createDataGrid( objectParams.viewBy ); let valueTypesDataSource = Fse.Data.newDataSource( { object : "OPA.valueTypes" , paginate : false, keyField : "fieldValue" } ); valueTypesDataSource.load().done( function( vt ) { console.log( "VALUE TYPES", vt ); instance.valueTypes = vt; let unitOfMeasureDS = new DevExpress.data.ArrayStore( { data : instance.valueTypes, key : "fieldValue" }); let unitOfMeasureDisplay = ''; unitOfMeasureDS.byKey( objectParams.uom ).done( function( data ) { unitOfMeasureDisplay = data.displayValue; }); let tytdCaption = `TYTD Actual ${unitOfMeasureDisplay}`; let lytdCaption = `LYTD Actual ${unitOfMeasureDisplay}`; instance.dataGrid.columnOption( "ytd", { caption : tytdCaption }); instance.dataGrid.columnOption( "lytd", { caption : lytdCaption }); let metaDataDataSource = Fse.Data.newDataSource( { object : "OPA.decliningPurchasing", paginate : false, objectParams : { returnMetaData : true } } ); metaDataDataSource.load().done( function( data ) { instance.dates = data[0]; instance.dataGrid.option( "dataSource", dataSource ); let lytdRangeDisplay; let tytdRangeDisplay; if( instance.dates.opa_windowMonths == 1 ) { lytdRangeDisplay = DevExpress.localization.formatDate( new Date( instance.dates.fiscalLastYearCutoff ), "MMM yyyy" ); tytdRangeDisplay = DevExpress.localization.formatDate( new Date( instance.dates.fiscalYearCutoff ), "MMM yyyy" ); } else { lytdRangeDisplay = DevExpress.localization.formatDate( new Date( instance.dates.fiscalLastYearStart ), "MMM" ) + '-' + DevExpress.localization.formatDate( new Date( instance.dates.fiscalLastYearCutoff ), "MMM yyyy" ); tytdRangeDisplay = DevExpress.localization.formatDate( new Date( instance.dates.fiscalYearStart ), "MMM" ) + '-' + DevExpress.localization.formatDate( new Date( instance.dates.fiscalYearCutoff ), "MMM yyyy" ); } instance.dataGrid.columnOption( "ytd", { caption : `${tytdCaption} (${tytdRangeDisplay})` }); instance.dataGrid.columnOption( "lytd", { caption : `${lytdCaption} (${lytdRangeDisplay})` }); //fiscalLastYearStart, fiscalLastYearEnd //fiscalYearStart, fiscalYearEnd // lastPurchasingMonth // windowMonths // valueType : quantity | dollars // choId // segmentId // territoryPath }) }) } DecliningOperatorPurchasingWidget.prototype.createDataGrid = function( viewBy ) { let instance = this; let columns = []; // group columns switch( viewBy ) { case "agreementOwner": columns.push( { dataField : "agreementOwnerName", caption : "Agreement Owner", viewBy:"agreementOwner" } ); break; case "operator": columns.push( { dataField : "operatorName", caption : "Operator", viewBy:"operator" } ); break; case "distributor": columns.push( { dataField : "dstName", caption : "Distributor", viewBy:"distributor" } ); break; case "territory": columns.push( { dataField : "territoryName", caption : "Territory", viewBy :"territory" } ); break; case "segment": columns.push( { dataField : "parentSegment", caption : "Segment", viewBy:"segment" }, { dataField : "segmentName", caption : "Sub-Segment", viewBy:"segment" } ) break; case "product": columns.push( { dataField : "product", caption : "Product", viewBy : "product" }, { dataField : "shortDesc", caption : "Description", viewBy : "product" } ) break; case "accountOwner": columns.push( { dataField : "accountOwnerFullName", caption : "Account Owner", viewBy : "accountOwner", cellTemplate( container, options ) { if( options.data.accountOwnerFullName ) { container.append( options.displayValue ) } else { container.append( $("").text( Fse.Portal.getConfiguration( "OPR.unassignedLabel" ) ) ); } } } ) break; case "manufacturer": columns.push( { dataField : "mfr_name", caption : "Manufacturer", viewBy : "manufacturer" } ) break; } // data columns columns.push( { dataField : "ytd", width : 220, caption : `TYTD Actual`, format : { type :"fixedPoint", precision : 0 } }, { dataField : "lytd", width : 220, caption : `LYTD Actual`, format : { type :"fixedPoint", precision : 0 } }, { dataField : "tyChange", width: 80, caption : "Change", format : { type :"fixedPoint", precision : 0 }, visible : false }, { dataField : "declinePct", width : 70, caption : "Decline", format : { type : "percent", precision : 1 } }, ); // extra data columns if( viewBy == "operator" ) { columns.push( { width : 100, dataField : "lastInteractionDate", caption : "Last Interaction", dataType : "date", format : "shortDate", visible : true , cellTemplate : function( container, options ) { if( options.data.nextInteractionDate ) { container.append( $("").text( "PND" ).attr( { "title" : DevExpress.localization.formatDate( new Date( options.data.nextInteractionDate ), "shortDate" )}) ) } else if( options.data.lastInteractionDate ) { container.append( DevExpress.localization.formatDate( new Date( options.data.lastInteractionDate ), "shortDate" )); } } }, { width : 130, dataField : "activeOpportunityVolume", caption : "Active Opp. Vol.", format : "fixedPoint", visible : true } ) } let dataGrid = $("
").dxDataGrid( { columns : columns, scrolling: { mode: 'virtual', }, onToolbarPreparing : function( e ) { let toolbarOptions = e.toolbarOptions; if( ! toolbarOptions.items ) toolbarOptions.items = []; toolbarOptions.items.push( { location : "after", widget : "dxButton", options : { icon : "export" , hint : "Export", onClick : function( te ) { Fse.UI.DataGridHelper.exportDataGrid( { dataGrid : e.component, fileName : "DecliningOperatorPurchasing.xlsx", onPrepareForExport : function( dataGrid ) {}, exportCriteria : null }); } } }) }, onContextMenuPreparing : function( e ) { if( e.target != "content") return; if( e.row.rowType != "data" ) return; let items = [ { text : "Go to Operator Purchasing", onClick : function( ee ) { instance.gotoOperatorAnalyzer( e.row.data ); } } ]; if( e.row.data.clientOperatorId ) { items.push( { text : "Plan Call", onClick : function( ee ) { Fse.CLOS.createAccountInteraction( e.row.data.clientOperatorId, 'OPR', function(){}, 0); } }) items.push( { text : "Go to Operator", onClick : function( ee ) { let partnerURL = Fse.CRM.buildGotoProfileURL( { partnerType : "OPR", partnerId : e.row.data.clientOperatorId, partnerTk : 0 }) window.location.href = partnerURL; } }); } if( items.length ) { if (!e.items) e.items = []; items.forEach( function( item ) { e.items.push( item ); }) } } }).dxDataGrid( "instance" ); return dataGrid; } DecliningOperatorPurchasingWidget.prototype.ready = function() { return this.readyPromise; } DecliningOperatorPurchasingWidget.prototype.gotoOperatorAnalyzer = function( data ) { let instance = this; let opaSearchReady = $.Deferred(); let opaSearch = { perspective : "Operator" }; // timeframe opaSearch.windowMonths = instance.dates.opa_windowMonths; opaSearch.lastPurchasingMonth = Fse.Date.formatDate( new Date( instance.dates.opa_lastPurchasingMonth )); if( instance.widgetOptions.config.uom ) { opaSearch.valueType = instance.widgetOptions.config.uom; } // widget filters if( instance.dataSourceObjectParams.productHierarchyPath ) { // need to convert produchHierarchyPaths into opaSearchParameters. let paths = instance.dataSourceObjectParams.productHierarchyPath; if( ! Array.isArray( paths )) { paths = [paths]; }; let pathsToResolve = paths.length; let productHierarchyPathStore = Fse.Data.newDataSource( { object : "PRD.productHierarchyPaths", keyField : "productHierarchyPath" }).store(); paths.forEach( function( pathToResolve ) { productHierarchyPathStore.byKey( pathToResolve ).done( function( php ) { pathsToResolve--; switch( php.catalogType ) { case "PRD" : if( ! opaSearch.prodId ) opaSearch.prodId = []; opaSearch.prodId.push( php.catalogId ); break; case "SKU" : if( ! opaSearch.skuId ) opaSearch.skuId = []; opaSearch.skuId.push( php.catalogId ); break; case "CAT" : if( ! opaSearch.categoryId ) opaSearch.categoryId = []; opaSearch.categoryId.push( php.catalogId ); break; case "LIN" : if( ! opaSearch.prodline_Id ) opaSearch.prodline_Id = []; opaSearch.prodline_Id.push( php.catalogId ); break; default: if( ! opaSearch.mfr_id ) opaSearch.mfr_id = []; opaSearch.mfr_id.push( php.mfr_id ); break; } if( pathsToResolve == 0 ) { opaSearchReady.resolve(); } }) }) } else { opaSearchReady.resolve(); } if( instance.dataSourceObjectParams.territoryPath ) { opaSearch.territoryPath = instance.dataSourceObjectParams.territoryPath; } if( instance.dataSourceObjectParams.clientSegPath ) { opaSearch.segmentPath = instance.dataSourceObjectParams.clientSegPath; } if( instance.dataSourceObjectParams.accountOwnerUserId ) { opaSearch.accountOwnerUserId = instance.dataSourceObjectParams.accountOwnerUserId; } if( instance.dataSourceObjectParams.mfr_id ) { opaSearch.mfr_id = instance.dataSourceObjectParams.mfr_id; } // perspective and drilldown filters switch( instance.widgetOptions.config.viewBy ) { case "operator" : opaSearch.perspective = "Product"; opaSearch.choId = data.cho_id; break; case "product" : opaSearch.perspective = "Operator"; opaSearch.prodId = data.prodId; delete opaSearch.productHierarchyPath; break; case "territory" : opaSearch.perspective = "Operator"; opaSearch.territoryPath = data.territoryPath; break; case "segment" : opaSearch.perspective = "Operator"; opaSearch.segmentPath = data.segmentPath; break; case "distributor" : opaSearch.perspective = "Operator"; opaSearch.distributorKeyId = data.distributorKeyId; break; case "accountOwner" : opaSearch.perspective = "Operator"; opaSearch.accountOwnerUserId = data.accountOwnerUserId; break; case "agreementOwner" : opaSearch.perspective = "Operator"; opaSearch.agreementOwnerChoId = data.agreementOwnerChoId; break; case "manufacturer" : opaSearch.perspective = "Operator"; opaSearch.mfr_id = data.mfr_id; break; } opaSearchReady.done( function() { console.log( "calling OPA with", opaSearch ); instance.widgetOptions.dashboard.tabSearch( "OperatorAnalyzer", opaSearch ) ; }) } DecliningOperatorPurchasingWidget.prototype.edit = function(applyFn) { let instance = this; if( ! instance.preferencesPopup ) { const items = []; // this should only support the values that opa allows // let allowedUnitsOfMeasure = []; // Fse.Portal.appConfiguration.CRM.allUnitsOfMeasure.forEach( function( item ) { // if( item.uom == "dollars" ) { // if( Fse.Portal.getConfiguration( "OPA.allowListDollars" ) == "true" ) { // allowedUnitsOfMeasure.push( item ); // } // } else { // allowedUnitsOfMeasure.push( item ); // } // }) if( instance.valueTypes.length > 1 ) { items.push( { dataField : "uom", label : { text : "Unit of Measure" }, editorType : "dxRadioGroup", editorOptions : { items : instance.valueTypes, layout : "horizontal", valueExpr : "fieldValue", displayExpr : "displayValue" } }); } items.push( { label : { location : "left", text : "Territory" }, dataField : "territoryPath", editorType : "dxDropDownBox", cssClass : instance.activeFilterPreferences.territoryPath ? 'fx-active-preference' : null, editorOptions : Fse.UI.multiSelectDropDownBoxEditorOptions( { dataSource : Fse.Data.newDataSource( { object : "TER.salesTerritories", keyField : "territoryPath" } ), searchExpr : "territoryPath", searchMode : "contains", displayExpr : "territoryPath", multipleSelectedDisplay : "Multiple Territories Selected", keyExpr : "territoryPath", title : "Select Territories" }) }); items.push( { label : { location : "left", text : "Account Owner" }, dataField : "accountOwnerUserId", editorType : "dxDropDownBox", cssClass : instance.activeFilterPreferences.accountOwnerUserId ? 'fx-active-preference' : null, editorOptions : Fse.UI.multiSelectDropDownBoxEditorOptions( { dataSource : Fse.Data.newDataSource( {object : "CRM.salesRepList", keyField : "valueId", objectParams : { version : 2 //,staffOnly : widget.widgetOptions.config.staffOpportunity } } ), searchExpr : "valueName", searchMode : "contains", displayExpr : "valueName", multipleSelectedDisplay : "Multiple Sales Reps Selected", keyExpr : "valueId", title : "Select Sales Reps" }) }); if( Fse.Portal.appConfiguration.STP.ownerType == "BRO" ) { items.push( { dataField : "mfr_id", label : { location : "left", text : "Manufacturer" }, editorType : "dxDropDownBox", cssClass : instance.activeFilterPreferences.mfr_id ? 'fx-active-preference' : null, editorOptions : Fse.UI.multiSelectDropDownBoxEditorOptions( { dataSource : Fse.Data.newDataSource( { 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( { dataField : "productHierarchyPath", label : { location : "left", text : "Product" }, editorType : "dxDropDownBox", cssClass : instance.activeFilterPreferences.productHierarchyPath ? 'fx-active-preference' : null, editorOptions : Fse.UI.multiSelectDropDownBoxEditorOptions( { dataSource : Fse.Data.newDataSource( { object : "PRD.productHierarchyPaths", keyField : "productHierarchyPath" } ), searchExpr : "productHierarchyPath", searchMode : "contains", displayExpr : "productHierarchyPath", multipleSelectedDisplay : "Multiple Product Hierarchy Paths Selected", keyExpr : "productHierarchyPath", title : "Select Product Hierarchy Path", searchTemplate : function( applyFilters ) { return Fse.UI.productHierarchyPathMultiSelectSearchTemplate( applyFilters ) } }) }); items.push( { label : { text : "Decline % Threshold" }, dataField : "declinePctThreshold", editorType : "dxSlider", editorOptions : { step : 5, tooltip: { enabled: true, format : function (value) { return `${value}%`; }, position: 'below', showMode : "onHover" } } }, { label : { text : "LYTD Vol. Threshold" }, dataField : "lytdVolumeThreshold", editorType : "dxNumberBox", editorOptions : { min : 0, showClearButton : true, width : 100 } } ) let viewByOptions = [ { viewBy : "operator", viewByDesc : "Operator" }, { viewBy : "accountOwner", viewByDesc : "Account Owner" }, { viewBy : "segment", viewByDesc : "Segment" }, { viewBy : "product", viewByDesc : "Product" }, { viewBy : "distributor", viewByDesc : "Distributor" }, { viewBy : "territory", viewByDesc : "Territory" } ]; if( Fse.Portal.appConfiguration.STP.ownerType == "MFR" ) { viewByOptions.push( { viewBy : "agreementOwner", viewByDesc : "Agreement Owner" } ); } else { viewByOptions.push( { viewBy : "manufacturer", viewByDesc : "Manufacturer" } ); } items.push( { dataField : "viewBy", label : { location : "left", text : "View By" }, editorType : "dxSelectBox", editorOptions : { dataSource : viewByOptions, displayExpr : "viewByDesc", valueExpr : "viewBy" } // editorType : "dxDropDownBox", // editorOptions : { // placeholder : "Select Columns to View By", // dataSource : viewByOptions, // valueExpr : "viewBy", // displayExpr : "viewByDesc", // contentTemplate : function( e ) { // const v = e.component.option( "value" ); // const $list = $("
").dxList( { // dataSource : e.component.getDataSource(), // keyExpr : "viewBy", // displayExpr : "viewByDesc", // selectionMode: "multiple", // showSelectionControls : true, // selectedItemKeys : v, // onSelectionChanged : function( sce ) { // const keys = sce.component.option( "selectedItemKeys" ); // e.component.option( "value", keys ); // } // }) // list = $list.dxList('instance'); // e.component.on('valueChanged', (args) => { // const { value } = args; // list.option( "selectedItemKeys", value ); // }); // return $list; // } // } } ); 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( { object : "SPL.segments", keyField : "clientSegPath" } ), searchExpr : "clientSegPath", searchMode : "contains", displayExpr : "clientSegPath", multipleSelectedDisplay : "Multiple Segments Selected", keyExpr : "clientSegPath", title : "Select Segments" }) }); // annotate the preference editor to let user know there are active filter preferences 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; } }) } const formData = { accountOwnerUserId : instance.widgetOptions.config.accountOwnerUserId, territoryPath : instance.widgetOptions.config.territoryPath, productHierarchyPath : instance.widgetOptions.config.productHierarchyPath, viewBy: instance.widgetOptions.config.viewBy, clientSegPath : instance.widgetOptions.config.clientSegPath, uom : instance.widgetOptions.config.uom, mfr_id : instance.widgetOptions.config.mfr_id, lytdVolumeThreshold : instance.widgetOptions.config.lytdVolumeThreshold, declinePctThreshold : instance.widgetOptions.config.declinePctThreshold } this.preferencesPopup = $("
").dxPopup( { title : "Preferences", width : 400, 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.option( "formData", formData ); return instance.preferencesForm.element(); }, toolbarItems : [ { 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(); let fd = instance.preferencesForm.option( "formData" ); applyFn( fd ); } } } ] } ).dxPopup( "instance" ); instance.preferencesPopup.element().appendTo( "body" ); } instance.preferencesPopup.show(); } Fse.Portal.addWidgetFactory( "DecliningOperatorPurchasingWidget", function( widgetDef ) { return new DecliningOperatorPurchasingWidget( widgetDef ); });