Creating a batch job with query based filter in D365 back-office

There are often business processes that requires us to run specific operations as a batch process. Dynamics 365 Head quarters (HQ) allows us to be able to periodically execute batch jobs.

To create a custom batch job you need to extend the RunBaseBatch class. The following lines of code show how we can create a simple export job. 

class InvoiceExport extends RunBaseBatch
{
    Query                   query;
    QueryRun                invoiceQueryRun; // QueryRun object that will be used to generate data for invoices to be exported.
    DialogGroup             dialogGroupSetup; // This is a dialog group.
    DialogField             dialogFieldDownloadFile; // This is a dialog field.
    boolean                 downloadFile; // This is a variable declaration that will store the user input.

    // You need to initialize the macro so that your initial dialog values are registered.
    // Note if this is an extension of an existing dialog then you
    #define.CurrentVersion(1)
    #localmacro.CurrentList
        downloadFile
    #endmacro

    public container pack()
    {
        return [#CurrentVersion, #CurrentList, invoiceQueryRun.pack()];
    }

    boolean unpack(container packedClass)
    {
        Version     version     = RunBase::getVersion(packedClass);
        container   packedQuery;

        switch (version)
        {
            case #CurrentVersion:
                [version, #CurrentList, packedQuery] = packedClass;

                if (SysQuery::isPackedOk(packedQuery))
                {
                    invoiceQueryRun = new QueryRun(packedQuery);
                }
                break;
            default:
                return false;
        }

        return true;
    }

    public Object dialog()
    {
        DialogRunbase dialog = super();

        dialogGroupSetup = dialog.addGroup('Invoice export');

        dialogFieldDownloadFile = dialog.addFieldValue(extendedTypeStr(NoYesId), downloadFile, 'Download File', 'Download File');

        return dialog;
    }

    public void dialogPostRun(DialogRunbase dialog)
    {
        super(dialog);
    }

    public boolean getFromDialog()
    {
        // This gets and sets the value for the dialog field based on user input.
        downloadFile   = dialogFieldDownloadFile.value();

        return super();
    }

    protected boolean canRunInNewSession()
    {
        return false;
    }

    private static ClassDescription  description()
    {
        return 'Invoice export'; // The dialog description.
    }

    public void initQueryRun()
    {
        this.initInvoiceQuery();
        invoiceQueryRun = new QueryRun(query);
    }
    
    public QueryRun queryRun()
    {
        return invoiceQueryRun;
    }

    public void run()
    {
        try
        {
            this.exportData();
        }
        catch (Exception::Deadlock)
        {
            retry;
        }
    }

    public boolean showQueryValues()
    {
        return true;
    }

    public static void main(Args _args)
    {
        InvoiceExport invoiceExport  = new InvoiceExport();

        invoiceExport.initQueryRun();

        if (invoiceExport.prompt())
        {
            invoiceExport.runOperation();
        }
    }

    private void initInvoiceQuery()
    {     
        query = new Query();

        QueryBuildDataSource custInvoiceTransQBDS = query.addDataSource(tableNum(CustInvoiceTrans));

        // join CustInvoiceJour
        QueryBuildDataSource custInvoiceJourQBDS = custInvoiceTransQBDS.addDataSource(tableNum(CustInvoiceJour));
        custInvoiceJourQBDS.joinMode(JoinMode::InnerJoin);
        custInvoiceJourQBDS.addLink(fieldNum(CustInvoiceTrans, ParentRecId), fieldNum(CustInvoiceJour, RecId));

        // join RetailCustInvoiceJourTable
        QueryBuildDataSource retailCustInvoiceJourTableQBDS = custInvoiceJourQBDS.addDataSource(tableNum(RetailCustInvoiceJourTable));
        retailCustInvoiceJourTableQBDS.joinMode(JoinMode::ExistsJoin);
        retailCustInvoiceJourTableQBDS.addLink(fieldNum(CustInvoiceJour, RecId), fieldNum(RetailCustInvoiceJourTable, CustInvoiceJour));

        // join RetailExternalIdToCustomerMap
        QueryBuildDataSource externalIdQBDS = custInvoiceJourQBDS.addDataSource(tableNum(RetailExternalIdToCustomerMap));
        externalIdQBDS.joinMode(JoinMode::ExistsJoin);
        externalIdQBDS.addLink(fieldNum(CustInvoiceJour, OrderAccount), fieldNum(RetailExternalIdToCustomerMap,CustomerAccountNumber));

        // join RetailChannelTable for all channels
        QueryBuildDataSource retailChannelTableQBDS = retailCustInvoiceJourTableQBDS.addDataSource(tableNum(RetailChannelTable));
        retailChannelTableQBDS.joinMode(JoinMode::ExistsJoin);
        retailChannelTableQBDS.addLink(fieldNum(RetailCustInvoiceJourTable, RetailChannel), fieldNum(RetailChannelTable, RecId));
    }

    private void exportData()
    {
        while (invoiceQueryRun.next())
        {
            CustInvoiceTrans    custInvoiceTrans    = invoiceQueryRun.get(tableNum(CustInvoiceTrans));
            CustInvoiceJour     custInvoiceJour     = invoiceQueryRun.get(tableNum(CustInvoiceJour));

            if (custInvoiceTrans && custInvoiceJour)
            {
                InventDim                   inventDim           = InventDim::find(custInvoiceTrans.InventDimId);
                CustTable                   custTable           = CustTable::find(custInvoiceJour.OrderAccount);

                // do something
            }
        }
    }
}

Comments

Popular posts from this blog

D365FO - Create a multi-select lookup in batch jobs

Generate and download a csv file using X++