Creating a module in Microsoft Dynamics 365: Adding Number Sequence Module to Parameters Form [Part 6]

This post is part of a series on creating a module within a Microsoft Dynamics 365 for Finance and Operations application. Links to the other parts are available in the first post: Creating a Module in Microsoft Dynamics 365: Laying the Foundation.

A module in Microsoft Dynamics 365 for Finance and Operations is a grouping of similar business processes and functions that allow users to conduct business. Examples include General Ledger, Accounts Receivable, Accounts Payable, and many others. Developers create new modules to group new functionality in an equivalent manner or to produce third-party solutions.

In this series, I am creating a fictitious module called Generic Application Solution (GAS). The assumption will be that the reader has an intermediate knowledge of development in Dynamics 365 as well as best practices. This post will build upon the last post by adding the number sequences tab to the parameters form.

It is best practice to add a number sequence tab to the parameters form for the number sequence module. This user interface element allows users to more easily set the number sequences for the module without needing to sort through all of the number sequences in the application.

Add the Data Source

The parameters form needs to have a data source for the NumberSequenceReference table. For this exercise, the data source will be named NumberSequenceReference. This data source will need to have NumberSequenceTable as a referenced data source. Additionally, developers will need to add the following methods to this new data source.

    void removeFilter()
    {
        runExecuteDirect = false;
        numbersequenceReference_ds.executeQuery();
    }
    void executeQuery()
    {
        if (runExecuteDirect)
        {
            super();
        }
        else
        {
            runExecuteDirect = true;
            this.queryRun(NumberSeqReference::buildQueryRunMulti(numberSequenceReference,
                                                                 tmpIdRef,
                                                                 numberSequenceTable,
                                                                 numberSequenceModules,
                                                                 scope));
            numbersequenceReference_ds.research();
        }
    }
    int active()
    {
        NumberSequenceDatatype numberSequenceDatatype;
        int ret;

        ret = super();

        buttonNumberSequenceGroup.enabled(numberSequenceReference.groupEnabled());
        numberSequenceDatatype = NumberSequenceDatatype::find(numberSequenceReference.NumberSequenceDatatype);

        if (SysCountryRegionCode::isLegalEntityInCountryRegion([ #isoPL ]))
        {
            plNumberSequenceGroupInvent.visible(true);
            plNumberSequenceGroupInvent.enabled(
                numberSequenceDatatype.DatatypeId == extendedTypeNum(PurchInternalPackingSlipId) ||
                numberSequenceDatatype.DatatypeId == extendedTypeNum(PackingSlipId));
        }
        else
        {
            plNumberSequenceGroupInvent.visible(false);
        }

        if (!SysCountryRegionCode::isLegalEntityInCountryRegion([#isoIT]))
        {
            TaxBookSectionId.visible(false);
        }

        ChronologicalNumberingSetup_W.enabled(   CustParameters::find().EnableChronologicalInvoiceNumbering_W
                                              && ChronologicalNumberingSetup_W::isEnabledForDataType(numberSequenceDatatype.DatatypeId));

        return ret;
    }
    public display TaxBookSectionId taxBookSectionId(NumberSequenceReference _numberSequenceReference)
    {
        NumberSequenceTable numberSequenceTableLocal;

        if (numberSequenceReference.NumberSequenceId)
        {
            select firstonly NumberSequence from numberSequenceTableLocal where numberSequenceTableLocal.RecId == _numberSequenceReference.NumberSequenceId;
        }

        return TaxBookSection::findVoucherSeries(numberSequenceTableLocal.RecId).TaxBookSectionId;
    }
    /// <summary>
    /// Validates the <c>NumberSequenceReference</c> buffer.
    /// </summary>
    /// <returns>True if the <c>NumberSequenceReference</c> is valid; otherwise, false</returns>
    public boolean validateWrite()
    {
        boolean ret = super();

        #ISOCountryRegionCodes

        NumberSequenceDatatype numberSequenceDataType;
        NumberSequenceTable numberSequenceTableLocal;

        if (ret && SysCountryRegionCode::isLegalEntityInCountryRegion([#isoHU]))
        {
            numberSequenceDataType = NumberSequenceDatatype::find(numberSequenceReference.NumberSequenceDatatype, false);
            if (numberSequenceDataType.DatatypeId == extendedTypeNum(TaxReimbursementDoc_HU))
            {
                numberSequenceTableLocal = numberSequenceReference.numberSequenceTable();

                if (numberSequenceTableLocal.Manual)
                {
                    ret = checkFailed(strFmt("@AccountsReceivable:NumSequenceNotAllManualEdit", "@GEE31852"));
                }
            }
        }

        return ret;
    }
    /// <summary>
    ///
    /// </summary>
    public void init()
    {
        super();

        NumberSequenceReference_ds.cacheAddMethod(identifierStr(taxBookSectionId));
        NumberSequenceReference_ds.cacheAddMethod(tableMethodStr(NumberSequenceReference, referenceHelp));
        NumberSequenceReference_ds.cacheAddMethod(tableMethodStr(NumberSequenceReference, referenceLabel));
    }

In addition to the above methods, developers will need to override the resolveReference and the lookupReference methods on the NumberSequenceId field on the data source.

        public Common resolveReference(FormReferenceControl _formReferenceControl)
        {
            NumberSequenceCode code = _formReferenceControl.filterValue(
                AbsoluteFieldBinding::construct(fieldStr(NumberSequenceTable, NumberSequence),
                tableStr(NumberSequenceTable))).value();

            // Do not call super as we're providing our own disambiguation logic.
            // resolvedRecord = super(_formReferenceControl);

            return NumberSequenceTable::findByNaturalKey(code, scope.getId(true));
        }
        public Common lookupReference(FormReferenceControl _formReferenceControl)
        {
            NumberSequenceTable selectedRecord;
            SysReferenceTableLookup sysTableLookup = SysReferenceTableLookup::newParameters(tableNum(NumberSequenceTable), _formReferenceControl, true);
            Query lookupQuery;

            // Do not call super as we're providing our own custom lookup logic.
            // selectedRecord = super(_formReferenceControl);

            // Display the Title and Department fields in the lookup form.
            sysTableLookup.addLookupfield(fieldNum(NumberSequenceTable, NumberSequence));

            // Create a custom Query that filters on NumberSequenceScope.
            lookupQuery = new Query();
            lookupQuery.addDataSource(tableNum(NumberSequenceTable)).addRange(fieldNum(NumberSequenceTable, NumberSequenceScope)).value(queryValue(scope.getId(true)));
            sysTableLookup.parmQuery(lookupQuery);

            // Return the record selected by the user.
            selectedRecord = sysTableLookup.performFormLookup();

            return selectedRecord;
        }

Add Member Variables to the Form

The following member variables should be added to the form.

    #ISOCountryRegionCodes

    boolean runExecuteDirect;
    TmpIdRef tmpIdRef;

    NumberSeqScope scope;
    NumberSeqApplicationModule numberSeqApplicationModule;
    container numberSequenceModules;

Initialize the Member Variables

The init method of the form is where the objects supporting the number sequence tab should be initialized. Add the following code to this method. Some parts of this code will not compile until after the user interface elements have been added.

    /// <summary>
    ///
    /// </summary>
    public void init()
    {
        this.numberSeqPreInit();

        GASParameters::find();

        super();

        this.numberSeqPostInit();
    }
    void numberSeqPostInit()
    {
        numberSequenceReference_ds.object(fieldNum(NumberSequenceReference, AllowSameAs)).visible(numberSeqApplicationModule.sameAsActive());
        referenceSameAsLabel.visible(numberSeqApplicationModule.sameAsActive());
    }
    void numberSeqPreInit()
    {
        runExecuteDirect = false;

        numberSequenceModules = [NumberSeqModule::GAS];
        numberSeqApplicationModule = new NumberSeqModuleGAS();
        scope = NumberSeqScopeFactory::createDataAreaScope();
        NumberSeqApplicationModule::createReferences(NumberSeqModule::GAS, scope);
        tmpIdRef.setTmpData(NumberSequenceReference::configurationKeyTableMulti(numberSequenceModules));
    }

Add the User Interface Elements

The user interface elements consist of a tab on the form. Under this tab, the user will find a grid that will list all of the number sequences for the module. To keep things simple, it is best to copy these user interface elements from other parameters form such as CustParameters and adjust it as appropriate for the module. For this exercise, I copied the TabNumberSeq tab from the CustParameters form.

Wrapping Up

In order for the number sequences to show in the grid, there will need to be a call to the load method of the NumberSeqModule class (which calls the loadModule method). There are multiple options to accomplish this action such as employing a runnable class that the system administrator executes after deploying the module. In this exercise, I added a button that performs this action.

    [Control("Button")]
    class InitializeSequences
    {
        /// <summary>
        ///
        /// </summary>
        public void clicked()
        {
            super();

            // Alert the user that this action only needs to be performed once
            // and to click okay to continue.
            if (Box::okCancel("@GAS:NumSeqModuleInitMessage", DialogButton::Cancel) == DialogButton::Cancel)
            {
                return;
            }

            NumberSeqModuleGAS module = new NumberSeqModuleGAS();

            module.load();
        }

    }

That concludes the steps needed to add the user interface elements of a number sequence module. However, at this point, there will not be any entries in the number sequence grid. For that, developers will need to create data types that use a number sequence and to add it to the number sequence module class. This will be the topic of the next post.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s