Wednesday, July 8, 2015

Allowing the value of 0.00 on a form for a mandatory field that is variable type Real

As you know, the null value in the database for a real number is 0.0. If you have a form where you have this field marked mandatory and want to allow 0.0 this causes issues because the form reads the 0.0 in as null and gives you the error "Field 'X' must be filled in." You will receive this error before it even hits any of your validation code to determine if 0.0 is actually a valid value for that field.

Here is how I got around this. In the validate() method on the control (it has to be at the control level - if you also want it on the datasource, you can add it there too) I commented out the super() and just allowed it to return true (you can also call  your own validateField() method here if you like). My field happened to have an edit method, so in the edit method I called validateField() and modifiedField() on the table. When modifiedField() returned, I checked the value of my field, if it returned from validate/modified successfully and it was still 0, then I set the showZero() property of the control on the form to true. This shows the user the 0 that they entered. Remember to set your control AutoDeclaration property to Yes so that you can use the field control in the code easily.

edit Field editField(boolean _set, Field  _edtField)
{
    ;

    if (_set)
    {
        Table.Field = _edtField;
        if  (Table.validateField(fieldNum(Table,Field)))
        {
            Table.modifiedField(fieldNum(Table, Field));
            edtField = _edtField; // edtField is a global variable on the form
         
            if(edtField == 0.0)
            {
                Field_Control.showZero(true);
            }
         
        }
    }

    return edtField;
}

Filter controls on a form with user initiated filters

I added filters to the standard work order form in Dynamics AX. They are checkboxes for what status the user would like to filter. However, if a user filtered their own fields using the filtering grid (CTRL-G) and then they checked or unchecked a status box, their user filters would disappear and the form would reset using the checkbox filters.
Here is how I fixed this:

Set the initial ProdStatus filter using the usage data of the user for the original checkboxes that will be used. Do this in the init() method of the form after getting the usage data and setting the controls.

    xSysLastValue::getLast(this);
    StatusCreated.value(bStatusCreated);
    StatusCostEstimated.value(bStatusCostEstimated);
    StatusScheduled.value(bStatusScheduled);
    StatusReleased.value(bStatusReleased);
    StatusStartedUp.value(bStatusStartedUp);
    StatusReportedFinished.value(bStatusReportedFinished);
    StatusCompleted.value(bStatusCompleted);

For each status, add it to the status string field you are building:

    if (bStatusCreated)
    {
        if (!strLen(statusFilter))
        {
            statusFilter += int2str(enum2int(ProdStatus::Created));
        }
        else
        {
            statusFilter += ',' + int2str(enum2int(ProdStatus::Created));
        }
    } . . .

Then set your query range:
    qbrStatus            = this.query().dataSourceTable(tablenum(ProdTable)).addRange(fieldnum  
                                                                                                              (ProdTable, ProdStatus));
    if (strLen(statusFilter))
    {
        qbrStatus.status(RangeStatus::Hidden);
        qbrStatus.value(statusFilter);
    }
    else
    {
        qbrStatus.value(SysQuery::valueUnlimited());
    }

In the modified method of the checkbox status controls, retrieve the prodTable_ds.queryRun().query() and then modify it with your current status checkbox. Then call ProdTable_DS.research(). Calling executeQuery() again was what was resetting the form to the original query. Using research() will keep all the user filters the same.

    str                     statusFilter;
    Query                   queryProdTable;
    QueryBuildDatasource    qbdsProdTable;
    ;
    if (!ProdTable_DS.queryRun()) // just in case
    {
        return;
    }
    queryProdTable       = ProdTable_DS.queryRun().query();
    qbdsProdTable        = queryProdTable.dataSourceTable(tableNum(ProdTable));

Just like in the init() method, for each status, add it to the statusFilter string and then set your status. This new qbrStatus is using the the query from queryRun() though so it will maintain user filters. Calling research() on the datasource will run this filtered query on your form.

    qbrStatus = SysQuery::findOrCreateRange(qbdsProdTable, fieldNum(ProdTable, ProdStatus));
    qbrStatus.status(RangeStatus::Hidden);
    qbrStatus.value(statusFilter);

    ProdTable_DS.research();