File import with SysOperationFramework dialog – D365 FO

This code allows you to create customers by uploading a text/csv file that contains a list of customers. And thanks to the encoding method, it also handles special characters without problems (you can set any character table in the processOperation method at the instantiation of “sr” variable.). This solution also handles UTF-8 and ANSI-encoded documents that contains special characters.

Contract class:

/// <summary>
/// Contract class of file uploader SysOperationFramework dialog.
/// </summary>
[DataContractAttribute,
 SysOperationContractProcessing(classStr(ImportDialogUIBuilder_DV))]
final class ImportDialogContract_DV
{
    container custList;

    /// <summary>
    /// Parameter method which holds values of the packed variables from <c>FileUploadTemporaryStorageResult</c> class.
    /// </summary>
    /// <param name = "_wage">Packed instance of <c>FileUploadTemporaryStorageResult</c> class</param>
    /// <returns>Container with packed values</returns>
    [DataMemberAttribute('FileUploadControl'),
     SysOperationLabelAttribute("Customer list")]
    public container parmCustList(container _custList =  custList)
    {
        custList = _custList;
        return custList;
    }
}

UiBuilder class:

/// <summary>
/// UI Builder class for file uploader SysOperationFramework dialog.
/// </summary>
final class ImportDialogUIBuilder_DV extends SysOperationAutomaticUIBuilder
{
    ImportDialogContract_DV   contract;

    #define.fileUpload('fileUpload')

    /// <summary>
    /// Overriden the <c>postBuild</c> method to add a <c>FileUpload</c> controls
    /// </summary>
    public void postBuild()
    {
        DialogGroup      fieldGroup;
        FormBuildControl fileUploadControl;
        FileUploadBuild  fileUploadBuild;

        super();

        contract = this.dataContractObject();

        fieldGroup = dialog.addgroup("Upload a customer list file.");

        fileUploadControl   = dialog.formBuildDesign().control(fieldGroup.name());
        fileUploadBuild     = fileUploadControl.addControlEx(classstr(FileUpload), #fileUpload);

        fileUploadBuild.style(FileUploadStyle::MinimalWithFilename);
        fileUploadBuild.baseFileUploadStrategyClassName(classstr(FileUploadTemporaryStorageStrategy));
        fileUploadBuild.fileTypesAccepted(".txt, .csv");
        fileUploadBuild.fileNameLabel("Customer list");
    }

    /// <summary>
    /// Subscribes events.
    /// </summary>
    /// <param name = "_formRun">Dialog form instance</param>
    private void dialogEventsSubscribe(FormRun _formRun)
    {
        FileUpload custList                   = _formRun.control(_formRun.controlId(#fileUpload));
        custList.notifyUploadCompleted        += eventhandler(this.uploadCompleted);
        custList.notifyUploadAttemptStarted   += eventhandler(this.uploadStarted);
        
        _formRun.onClosing += eventhandler(this.dialogClosing);
    }

    /// <summary>
    /// Unsubscribes the registered events.
    /// </summary>
    /// <param name = "sender">xFormRun of dialog</param>
    /// <param name = "e">FormEventArgs of dialog</param>
    [SuppressBPWarning('BPParameterNotUsed', "Parameter required")]
    private void dialogClosing(xFormRun sender, FormEventArgs e)
    {
        this.dialogEventsUnsubscribe(sender as FormRun);
    }

    /// <summary>
    /// Unsubscribes events from the dialog form.
    /// </summary>
    /// <param name = "_formRun">The instance of the dialog form</param>
    private void dialogEventsUnsubscribe(FormRun _formRun)
    {
        FileUpload custList       = _formRun.control(_formRun.controlId(#fileUpload));

        custList.notifyUploadCompleted        -= eventhandler(this.uploadCompleted);
        custList.notifyUploadAttemptStarted   -= eventhandler(this.uploadStarted);
        
        _formRun.onClosing                          -= eventhandler(this.dialogClosing);
    }

    /// <summary>
    /// Executes additional logic once the upload of the file is completed.
    /// </summary>
    private void uploadCompleted()
    {
        FormRun                             formRun                 = this.dialog().dialogForm().formRun();
        FileUpload                          custList                = formRun.control(formRun.controlId(#fileUpload));
        FileUploadTemporaryStorageResult    custListUploadResult    = custList.getFileUploadResult();

        if (custListUploadResult != null && custListUploadResult.getUploadStatus())
        {
            contract.parmCustList(custListUploadResult.pack());
        }

        this.setDialogOkButtonEnabled(formRun, true);
    }

    /// <summary>
    /// Additional logic which is executed once the upload of the file has started.
    /// </summary>
    private void uploadStarted()
    {
        FormRun formRun = this.dialog().dialogForm().formRun();
        this.setDialogOkButtonEnabled(formRun, false);
    }

    /// <summary>
    /// Enables or disables the OK button.
    /// </summary>
    /// <param name = "_formRun">Dialog form instance</param>
    /// <param name = "_isEnabled">True if OK button should be enabled, false if shouldn't.</param>
    private void setDialogOkButtonEnabled(FormRun _formRun, boolean _isEnabled)
    {
        FormControl okButtonControl = _formRun.control(_formRun.controlId("CommandButton"));

        if (okButtonControl)
        {
            okButtonControl.enabled(_isEnabled);
        }
    }

    /// <summary>
    /// Adds event subscriptions.
    /// </summary>
    public void postRun()
    {
        super();

        FormRun formRun = this.dialog().dialogForm().formRun();
        this.dialogEventsSubscribe(formRun);

        this.setDialogOkButtonEnabled(formRun, false);
    }
}

Controller class:

/// <summary>
/// Controller class of file uploader dialog.
/// </summary>
final class ImportDialogController_DV extends SysOperationServiceController
{
    protected void new()
    {
        super();
     
        this.parmClassName(classStr(ImportDialogService_DV));
        this.parmMethodName(methodStr(ImportDialogService_DV, processOperation));
    }

    /// <summary>
    /// Method which is run while calling the corresponding menu item
    /// </summary>
    /// <param name = "args">Arguments passed from the menu item</param>
    public static void main(Args args)
    {
        ImportDialogController_DV controller;
     
        controller = new ImportDialogController_DV();
        controller.parmLoadFromSysLastValue(false);

        controller.startOperation();
    }

    /// <summary>
    /// Gets or sets the caption of the dialog
    /// </summary>
    /// <param name = "_dialogCaption">The caption to set for the dialog</param>
    /// <returns>The caption of the dialog</returns>
    public LabelType parmDialogCaption(LabelType _dialogCaption = dialogCaption)
    {
        LabelType ret;
    
        ret = super(_dialogCaption);

        ret = "Customer list import";
        
        return ret;
    }
}

Service class:

/// <summary>
/// Service class with file handling and data insertion logic.
/// </summary>
final class ImportDialogService_DV extends SysOperationServiceBase
{
    #Define.separator(',')

    /// <summary>
    /// The code that processes the file
    /// </summary>
    /// <param name = "_contract">Instance of the <c>ImportDialogContract_DV</c> class</param>
    public void processOperation(ImportDialogContract_DV _contract)
    {
        FileUploadTemporaryStorageResult    fileUploadResult = new FileUploadTemporaryStorageResult();
        System.IO.Stream                    readStream;
        System.IO.StreamReader              sr;
        CustTable                           custTable;
        InteropPermission                   interopPermission;
        str                                 srLine;
        container                           line;
        List                                lineList;
        RecordInsertList                    recordInsertList;
            
        if (_contract.parmCustList() != conNull())
        {
            fileUploadResult.unpack(_contract.parmCustList());

            readStream          = File::UseFileFromURL(fileUploadResult.getDownloadUrl());
            sr                  = new System.IO.StreamReader(readStream, System.Text.Encoding::GetEncoding(1250));
            lineList            = new List(Types::AnyType);

            if(sr.Peek() == -1)
            {
                return;
            }

            interopPermission = new InteropPermission(InteropKind::ClrInterop);
            interopPermission.assert();
        
            srLine      = sr.ReadLine();

            while(!System.String::IsNullOrEmpty(srLine))
            {
                lineList    = strSplit(srLine, #separator);
                line        = list2Con(lineList);

                if(!line)
                {
                    continue;
                }

                custTable.clear();
                custTable.AccountNum    = conPeek(line, 1);
                custTable.CustGroup     = conPeek(line, 2);
                custTable.Currency      = conPeek(line, 3);
                custTable.insert(DirPartyType::None, conPeek(line, 4));
                custTable.initValue();

                srLine = sr.ReadLine();
            }
        }
    }
}

With this code we will get a dialog window with a simple fileUpload control where the user can choose the txt/csv file from his/her local computer after clicking the “Browse” button.

Picture of the file upload dialog.
Result

You can read more about SysOperationFramework on the following link: SysOperation FrameWork in D365 for Operations – TheDynamicsDrive (wordpress.com).

Leave a Reply

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