EDIT: Dynamic Parameters are now supported with the introduction of the Saleslogix Reporting Assistant introduced in v8.1 SNC 03b. However the instructions below work for previous versions which aren’t on the SNC 03b update.

An important current shortcoming of reporting on Saleslogix 8.1 (now known as Infor CRM) is that dynamic parameters are not supported. Those are often used to provide the user with a list of possible values loaded from the database, instead of having to hard-code them in the report. This worked well enough (kind of – the Crystal Reports dynamic parameters have some limitations themselves), but when running the report on 8.1 you will only see this lovely error:

Capture

Thankfully Swiftpage actually provided us with a way to add our own custom parameter prompts and integrate them with the standard report interface – something which was rather painful to do on previous versions. This is done similar to other javascript customizations, by overriding the default object’s prototype to inject our custom behavior. In this case the “CrystalReportParametersDialog” object is the one that is responsible for creating the widget corresponding to a specific parameter editor and this is where we can inject our custom widget based on the report and parameter names:

// Inject code to bring up the parameter prompt when the user wants to print the "My Report" report
define(['dojo/_base/lang', 'dojo/_base/declare', 'dojo/aspect', 'Sage/MainView/ReportMgr/Crystal/CrystalReportParametersDialog', './MyCustomReportParameter'],
function (lang, declare, aspect, CrystalReportParametersDialog, MyCustomReportParameter) {
    aspect.around(CrystalReportParametersDialog.prototype, "_getParameterEditor", function (originalFun) {
        // register a handler to return a custom editor for certain parameter / report combinations
        return function (parameter) {
            switch (this._reportMetadata.name) {
                case "My Report":
                    switch (parameter.name) {
                        case "Custom Parameter":
                            return new MyCustomReportParameter(parameter);
                    }
                    break;
            }
            // default behavior
            return originalFun.apply(this, arguments);
        }
    });
});

Here the “Custom Parameter” parameter will be prompted using our custom widget, other parameters on the report (or other report) will continue to have the default widget used by Swiftpage’s implementation. To create the editor widget one must inherit from the _ParameterEditorBase class. This example will prompt for a product name using a dropdown, and pass the selected product’s id to the report:

// Custom parameter prompt for product
define(['dojo/_base/lang', 'dojo/_base/declare', 'dojo/aspect', 'Sage/Utility',
    'Sage/Data/BaseSDataStore',
    'Sage/MainView/ReportMgr/Crystal/_ParameterEditorBase',
    'dojo/text!./templates/MyCustomReportParameter.html'],
function (lang, declare, aspect, Utility,
BaseSDataStore,
_ParameterEditorBase,
template) {
    return declare([_ParameterEditorBase], {
        widgetTemplate: Utility.makeTemplateFromString(template),
        value: null,
        _setValueAttr: function (value) {
            this._set("value", value);
        },
        _getValueAttr: function () {
            var self = this;
            this._promptParameter.currentValues = [];
            var currentValue = this._getCurrentValue();
            if (currentValue === null) {
                currentValue = "";
            }
            var parameterValue = self._getParameterValue(currentValue);
            self._promptParameter.currentValues.push(parameterValue);
            return this._promptParameter;
        },

        /**
        * This is a last method in the initialization process. 
        * It is called after all the child widgets are rendered so it's good for a container widget to finish it's post rendering here. 
        * This is where the container widgets could for example set sizes of their children relative to it's own size.
        */
        startup: function () {
            this.inherited(arguments);

            this._loadProducts();
        },
        isValid: function () {
            return true;
        },
        _getCurrentValue: function () {
            var id = this.cmbValues.get('value');
            return id;
        },

        _loadProducts: function () {
            var svc = Sage.Data.SDataServiceRegistry.getSDataService('dynamic');
            var req = new Sage.SData.Client.SDataResourceCollectionRequest(svc)
                .setResourceKind("products")
                .setQueryArg("orderBy", "Name");
            var cmbValues = this.cmbValues;
            req.read({
                success: function (data) {
                    var items = data.$resources;
                    for (var i = 0; i < items.length; i++) {
                        cmbValues.addOption({ label: items[i].Name, value: items[i].$key });
                    }
                }
            });
        },
        _getParameterValue: function (optionText) {
            var value = {
                className: "CrystalReports.ParameterFieldDiscreteValue",
                computedText: '"' + optionText + '"',
                description: optionText,
                displayText: optionText,
                discreteValue: {
                    actualValue: optionText,
                    actualValueType: "String",
                    value: optionText,
                    valueType: "String"
                }
            };
            return value;
        }
    });
});

With the accompanying template file, very simple:

<div>
    <div dojoattachpoint="promptParameterContainer" style="border: 1px solid #B5BCC7; padding: 10px">
        <div style="border-bottom: 1px solid #B5BCC7; padding-bottom: 5px">
            <div style="float: left; padding-top: 3px">
                <label for="values">Product:</label>
            </div>
            <div dojoattachpoint="divSingleValue" style="padding-top: 0px;">
                <select data-dojo-type="dijit.form.Select" dojoattachpoint="cmbValues" style="width: 150px">
                </select>                    
            </div>
        </div>
    </div>
</div>

The result is a prompt that is completely integrated with the standard UI, thus no disruption of the other option (file format, other conditions, etc):

Capture1