Running an Electronic Report from a menu item

Earlier I discussed how to create a new type of Electronic Report in Dynamics 365 for Operations (AX7), which can be viewed here,  Creating new Electronic Report. In this post, I want to look at how to modify the application so that a new Electronic Report can be run from a form using a menu item.

First of all, a class needs to be created which contains constants (yes, surprisingly, it’s done with constants), for the name of the model and the name of mapping.

Electronic Report Model

Electronic Report Mapping

class KRTEDeliveryNoteConstants
{
   public const str ERModelNameDeliveryNote        = 'DeliveryNote';
   public const str ERModelMappingNameDeliveryNote = 'DeliveryNoteCustomer'; 
}

Next, a parameter should be created for selecting the format to be used. A new field must be created, (in this example, I’m adding it to Sales Parameters), which will be a Foreign Key to the table,  ERFormatMappingTable.

image

Also, a new control in the parameters form has to be created, along with an event for the lookup.

image

The ERFormatMappingTable::lookup() is the method that will create the right query.  All you need to do, is pass your Model and Mapping names that were defined in the class with constants.

[FormControlEventHandler(formControlStr(CustParameters, KRTElectronicReporting_KRTERFormatMappingSalesDeliveryNote), FormControlEventType::Lookup)]
public static void KRTElectronicReporting_KRTERFormatMappingSalesDeliveryNote_OnLookup(FormControl _sender, FormControlEventArgs _e)
{
    FormControlCancelableSuperEventArgs ce = _e as FormControlCancelableSuperEventArgs;    //Method accepts control, model name and mapping name (names are defined in constants class)
    ERFormatMappingTable::lookup(_sender, KRTEDeliveryNoteConstants::ERModelNameDeliveryNote, KRTEDeliveryNoteConstants::ERModelMappingNameDeliveryNote);
 
    //cancel super() to prevent additional lookup popup.
    ce.cancelSuperCall();
}

For the lookup to be able to find the proper formats, you need:

  1. An Electronic Reporting Model with a specified name whose status is “Completed”
  2. An Electronic Reporting Model Mapping with the specified name
  3. An Electronic Reporting Format based on the specified model whose status is “Completed”

Now, a button must be created on the CustPackingSlipJournal form. I want to build this using a form extension, therfore I have to subscribe to an event.

[FormControlEventHandler(formControlStr(CustPackingSlipJournal, SendXMLOriginal), FormControlEventType::Clicked)]
    public static void SendXMLOriginal_OnClicked(FormControl _sender, FormControlEventArgs _e)
    {
        FormDataSource      formDataSource         = _sender.formRun().dataSource();
        CustPackingSlipJour custPackingSlipJour  = formDataSource.cursor();
 
        if (SalesParameters::find().KRTERFormatMappingSalesDeliveryNote)
        {
            Args args = new Args();
            args.record(custPackingSlipJour);
 
            new MenuFunction(menuitemOutputStr(KRTEDeliveryNoteGeneration), MenuItemType::Output).run(args);
 
            info("Delivery note added to the queue for processing. The file generation may take a while depending on the batch job setup");
        }
        else
        {
            warning(strFmt("Data model mapping is not set for delivery note"));
        }
    }

The menu item KRTEDeliveryNoteGeneration, which is called in the code above is an Output item that is connected to the controller class.

Now let’s look at the operating classes. Most noteworthy is that it is a pretty much standard SysOperationFramework implementation with the contract class extending the ERFormatMappingRunBaseContract.

Data contract

The Contract contains two main things – The Format Mapping Id, that is selected from the parameter we added earlier, and any values for filtering your data selection (if you have query configured for you model).

Note: The parent class (ERFormatMappingRunBaseContract) includes a comment that states “THIS IS AN INTERNAL CLASS, API COMPATIBILITY IS NOT GUARANTEED IN THE FUTURE.”, so even though we should use it to create a new type of Electronic Report, be aware that this might change in the future.

[DataContractAttribute]
class KRTEDeliveryNoteGenerationDataContract extends ERFormatMappingRunBaseContract
{
    private RecId                                   deliveryNoteRecId;
    private ERFormatMappingId          eRFormatMappingId;  
 
    public void initFromArgs(Args _args)
    {
        this.parmDeliveryNoteRecId(_args.record().RecId);
        this.parmERFormatMappingId(SalesParameters::find().KRTERFormatMappingSalesDeliveryNote);
    }
 
    [DataMemberAttribute]
    public RecId parmDeliveryNoteRecId(RecId _recId = deliveryNoteRecId)
    {
        deliveryNoteRecId = _recId;
        return deliveryNoteRecId;
    }
 
    [DataMemberAttribute]
    public ERFormatMappingId parmERFormatMappingId(ERFormatMappingId _eRFormatMappingId = eRFormatMappingId)
    {
        eRFormatMappingId = _eRFormatMappingId;
        return eRFormatMappingId;
    }
 
}

Controller 

Initializes the generation of the data contract and the running service method.

class KRTEDeliveryNoteGenerationController
{
    public ClassDescription caption()
    {
        return "Send eDeliveryNote XML";
    }
 
    public static void main(Args _args)
    {
        var contract = new KRTEDeliveryNoteGenerationDataContract();
        contract.initFromArgs(_args);
 
        var service = new KRTEDeliveryNoteGenerationService();
        service.generateEDeliveryNote(contract);
    }
 
}

Service class 

If you have a query-based model,  the Service method should contain context initialization , otherwise you just need to run ERFormatMappingRun::constructByFormatMappingId() with the selected format and without context(parameters class).

class KRTEDeliveryNoteGenerationService extends SysOperationServiceBase
{                  
    const Caption formatMappingRunJobCaption = "Send the eDevlieryNote XML";
 
    public void generateEDeliveryNote(KRTEDeliveryNoteGenerationDataContract _contract)
    {
        ERFormatMappingId              eRFormatMappingId    = _contract.parmERFormatMappingId();
        RecId                          deliveryNoteRecId    = _contract.parmDeliveryNoteRecId();
 
        ERModelDefinitionDatabaseContext            context       = new ERModelDefinitionDatabaseContext();
        ERModelDefinitionParamsUIActionComposite    parameters    = new ERModelDefinitionParamsUIActionComposite();
 
        context.addValue(tableNum(CustPackingSlipJour), fieldNum(CustPackingSlipJour, RecId), deliveryNoteRecId);
 
        parameters.add(context);
 
        ERFormatMappingRun::constructByFormatMappingId(eRFormatMappingId)
            .withParameter(parameters)
            .withFileDestination(_contract.getFileDestination())
            .withFormatMappingRunJobCaption(formatMappingRunJobCaption)
            .withRunInBatchMode(true)
            .run();
    }
 
}

Final thoughts:

In conclusion, for this modification, I used the standard Electronic Invoice functionality (EInvoiceGeneration… classes) as a model, but it seems like Microsoft doesn’t really want us to use this as a Framework.  Therefore, care should be taken as it may change in the future (the data contract in particular). Also, I didn’t find a way to run this as a recurring batch job since the framework only supports sending a file directly and does not serve the purpose of data synchronization with an external system.  However, it can be used in this way if run from the Electronic Reporting configuration form.

One thought on “Running an Electronic Report from a menu item

  1. Hello, we don’t have out-of-the box functionality to send reports, but if one run them it batch mode we add results as document handling attaches to Organization administration \ Electronic reporting \ Electronic reporting jobs

Leave a Reply

Your email address will not be published. Required fields are marked *