Friday, December 1, 2023

Initializes form controls at runtime using X++ D365fo

/// <summary>
    /// Initializes LATAM form controls.
    /// </summary>
    /// <param name="_sender">The form run.</param>
    /// <param name="_args">The form event args.</param>
    [FormEventHandler(formStr(SalesEditLines), FormEventType::Initialized), SuppressBPWarning('BPParameterNotUsed', 'False positive')]
    public static void SalesEditLines_OnInitialized(xFormRun _sender, FormEventArgs _args)
    {
        if (!FeatureStateProvider::IsFeatureEnabled(LTMGlobalizationFeature::instance()))
        {
            return;
        }

        FormRun formRun = _sender;
        LTMSalesEditLines ltmSalesEditLines = formRun.parmLTMSalesEditLines();
        Struct controlsInv = new Struct();
        Struct controlsPS = new Struct();
        DocumentStatus documentStatus = formRun.args().caller().documentStatus();
        FormTabPageControl LTMTab = formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMTab)));

        // Dummies
        FormStringControl LTMPSBankGroupId =  LTMTab.addControl(FormControlType::String, 'LTMPSBankGroupId');
        FormComboBoxControl LTMPSCPDSource = LTMTab.addControl(FormControlType::ComboBox, 'LTMPSCPDSource');
        FormStringControl LTMPSControlCode =  LTMTab.addControl(FormControlType::String, 'LTMPSControlCode');
        FormStringControl LTMPSBeneficiary =  LTMTab.addControl(FormControlType::String, 'LTMPSBeneficiary');
        FormStringControl LTMPSHolderAccountNum =  LTMTab.addControl(FormControlType::String, 'LTMPSHolderAccountNum');
        FormRealControl LTMPSWithholdingBaseMST = LTMTab.addControl(FormControlType::Real, 'LTMPSWithholdingBaseMST');
        FormRealControl LTMPSWithholdAccruedAmountMST = LTMTab.addControl(FormControlType::Real, 'LTMPSWithholdAccruedAmountMST');
        FormRealControl LTMPSWithholdAccruedBaseMST = LTMTab.addControl(FormControlType::Real, 'LTMPSWithholdAccruedBaseMST');
        FormRealControl LTMPSAmountMST = LTMTab.addControl(FormControlType::Real, 'LTMPSAmountMST');
        FormRealControl LTMPSWithholdingEffectiveRate = LTMTab.addControl(FormControlType::Real, 'LTMPSWithholdingEffectiveRate');

        FormStringControl LTMInvBankGroupId =  LTMTab.addControl(FormControlType::String, 'LTMInvBankGroupId');
        FormComboBoxControl LTMInvCPDSource = LTMTab.addControl(FormControlType::ComboBox, 'LTMInvCPDSource');
        FormStringControl LTMInvControlCode =  LTMTab.addControl(FormControlType::String, 'LTMInvControlCode');
        FormStringControl LTMInvBeneficiary =  LTMTab.addControl(FormControlType::String, 'LTMInvBeneficiary');
        FormStringControl LTMInvHolderAccountNum =  LTMTab.addControl(FormControlType::String, 'LTMInvHolderAccountNum');
        FormRealControl LTMInvWithholdingBaseMST = LTMTab.addControl(FormControlType::Real, 'LTMInvWithholdingBaseMST');
        FormRealControl LTMInvWithholdAccruedAmountMST = LTMTab.addControl(FormControlType::Real, 'LTMInvWithholdAccruedAmountMST');
        FormRealControl LTMInvWithholdAccruedBaseMST = LTMTab.addControl(FormControlType::Real, 'LTMInvWithholdAccruedBaseMST');
        FormRealControl LTMInvAmountMST = LTMTab.addControl(FormControlType::Real, 'LTMInvAmountMST');
        FormRealControl LTMInvWithholdingEffectiveRate = LTMTab.addControl(FormControlType::Real, 'LTMInvWithholdingEffectiveRate');

        LTMPSBankGroupId.visible(false);
        LTMPSCPDSource.visible(false);
        LTMPSControlCode.visible(false);
        LTMPSBeneficiary.visible(false);
        LTMPSHolderAccountNum.visible(false);
        LTMPSWithholdingBaseMST.visible(false);
        LTMPSWithholdAccruedAmountMST.visible(false);
        LTMPSWithholdAccruedBaseMST.visible(false);
        LTMPSAmountMST.visible(false);
        LTMPSWithholdingEffectiveRate.visible(false);

        LTMInvBankGroupId.visible(false);
        LTMInvCPDSource.visible(false);
        LTMInvControlCode.visible(false);
        LTMInvBeneficiary.visible(false);
        LTMInvHolderAccountNum.visible(false);
        LTMInvWithholdingBaseMST.visible(false);
        LTMInvWithholdAccruedAmountMST.visible(false);
        LTMInvWithholdAccruedBaseMST.visible(false);
        LTMInvAmountMST.visible(false);
        LTMInvWithholdingEffectiveRate.visible(false);
       
        ltmSalesEditLines.parmForm(formRun.form());
        ltmSalesEditLines.parmTabLTM(formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMTab))));
        ltmSalesEditLines.parmSelectedTableAux(0);
        ltmSalesEditLines.parmDocumentStatus(documentStatus);
        ltmSalesEditLines.parmProforma(formRun.args().caller().proforma());
        ltmSalesEditLines.parmCheckLTM(formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPrintFormLetter))));
        ltmSalesEditLines.parmPrimaryDS(formRun.dataSource(formDataSourceStr(SalesEditLines, SalesParmTable)));
        ltmSalesEditLines.parmExtendDS(formRun.dataSource(formDataSourceStr(SalesEditLines, LTMSalesParmTableJour)));
        ltmSalesEditLines.parmExtendDS2(formRun.dataSource(formDataSourceStr(SalesEditLines, LTMSalesParmTablePS)));
        ltmSalesEditLines.parmGroupJour(formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvoiceGrp))));
        ltmSalesEditLines.parmGroupPS(formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPackingSlipGrp))));
        
        // Packing Slip
        controlsPS.add(LTMTransBasicConst::LTMDocumentClassificationId, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSVoucherClassId))));
        controlsPS.add(LTMTransBasicConst::LTMSalesPointId, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSSalesPointId))));
        controlsPS.add(LTMTransBasicConst::LTMSalesPointPrefix, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSSalesPointPrefix))));
        controlsPS.add(LTMTransBasicConst::LTMDocumentNum, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSDocumentNum))));
        controlsPS.add(LTMTransBasicConst::LTMCompleteDocumentNum, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSCompleteDocumentNum))));
        controlsPS.add(LTMTransBasicConst::LTMDocumentDate, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSDocumentDate))));
        controlsPS.add(LTMTransBasicConst::LTMCAICAEDueDate, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSCAICAEDueDate))));
        controlsPS.add(LTMTransBasicConst::LTMDueDate, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSDueDate))));
        controlsPS.add(LTMTransBasicConst::LTMCAICAE, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSCAICAE))));
        controlsPS.add(LTMTransBasicConst::LTMCountryDocTypeId, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSCountryDocTypeId))));
        controlsPS.add(LTMTransBasicConst::LTMCountryDocNum, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSCountryDocNum))));
        controlsPS.add(LTMTransBasicConst::LTMBussinessName, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSName))));
        controlsPS.add(LTMTransBasicConst::LTMConcept1, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSConcept1))));
        controlsPS.add(LTMTransBasicConst::LTMConcept2, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSConcept2))));
        controlsPS.add(LTMTransBasicConst::LTMConcept3, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSConcept3))));
        controlsPS.add(LTMTransBasicConst::LTMFieldList01, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSListField01))));
        controlsPS.add(LTMTransBasicConst::LTMFieldList02, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSListField02))));
        controlsPS.add(LTMTransBasicConst::LTMFieldList03, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSListField03))));
        controlsPS.add(LTMTransBasicConst::LTMFieldList04, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSListField04))));
        controlsPS.add(LTMTransBasicConst::LTMFieldList05, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSListField05))));
        controlsPS.add(LTMTransBasicConst::LTMFieldList06, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSListField06))));
        controlsPS.add(LTMTransBasicConst::LTMFieldList07, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSListField07))));
        controlsPS.add(LTMTransBasicConst::LTMFieldList08, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSListField08))));
        controlsPS.add(LTMTransBasicConst::LTMFieldList09, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSListField09))));
        controlsPS.add(LTMTransBasicConst::LTMFieldList10, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSListField10))));
        controlsPS.add(LTMTransBasicConst::CountryRegionId,formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSCountryRegionId))));
        controlsPS.add(LTMTransBasicConst::StateId,formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSStateId))));
        controlsPS.add(LTMTransBasicConst::LTMStateDocTypeId,formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSStateDocTypeId))));
        controlsPS.add(LTMTransBasicConst::LTMStateDocNum,formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSStateDocNum))));
        controlsPS.add(LTMTransBasicConst::LTMTaxPayerTypeId,formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMPSTaxPayerTypeId))));

        // Dummy fields
        controlsPS.add(LTMTransBasicConst::BankGroupId,LTMPSBankGroupId);
        controlsPS.add(LTMTransBasicConst::LTMCPDSource,LTMPSCPDSource);
        controlsPS.add(LTMTransBasicConst::LTMControlCode,LTMPSControlCode);
        controlsPS.add(LTMTransBasicConst::LTMBeneficiary,LTMPSBeneficiary);
        controlsPS.add(LTMTransBasicConst::LTMHolderAccountNum,LTMPSHolderAccountNum);
        controlsPS.add(LTMTransBasicConst::LTMWithholdingBaseMST,LTMPSWithholdingBaseMST);
        controlsPS.add(LTMTransBasicConst::LTMWithholdAccruedAmountMST,LTMPSWithholdAccruedAmountMST);
        controlsPS.add(LTMTransBasicConst::LTMWithholdAccruedBaseMST,LTMPSWithholdAccruedBaseMST);
        controlsPS.add(LTMTransBasicConst::AmountMST,LTMPSAmountMST);
        controlsPS.add(LTMTransBasicConst::LTMWithholdingEffectiveRate,LTMPSWithholdingEffectiveRate);
        
        // Invoice
        controlsInv.add(LTMTransBasicConst::LTMDocumentClassificationId, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvVoucherClassId))));
        controlsInv.add(LTMTransBasicConst::LTMSalesPointId, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvSalesPointId))));
        controlsInv.add(LTMTransBasicConst::LTMSalesPointPrefix, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvSalesPointPrefix))));
        controlsInv.add(LTMTransBasicConst::LTMDocumentNum, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvDocumentNum))));
        controlsInv.add(LTMTransBasicConst::LTMCompleteDocumentNum, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvCompleteDocumentNum))));
        controlsInv.add(LTMTransBasicConst::LTMDocumentDate, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvDocumentDate))));
        controlsInv.add(LTMTransBasicConst::LTMCAICAEDueDate, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvCAICAEDueDate))));
        controlsInv.add(LTMTransBasicConst::LTMDueDate, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvDueDate))));
        controlsInv.add(LTMTransBasicConst::LTMCAICAE, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvCAICAE))));
        controlsInv.add(LTMTransBasicConst::LTMControlCode, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMControlCode))));
        controlsInv.add(LTMTransBasicConst::LTMCountryDocTypeId, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvCountryDocTypeId))));
        controlsInv.add(LTMTransBasicConst::LTMCountryDocNum, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvCountryDocNum))));
        controlsInv.add(LTMTransBasicConst::LTMBussinessName, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvName))));
        controlsInv.add(LTMTransBasicConst::LTMConcept1, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvConcept1))));
        controlsInv.add(LTMTransBasicConst::LTMConcept2, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvConcept2))));
        controlsInv.add(LTMTransBasicConst::LTMConcept3, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvConcept3))));
        controlsInv.add(LTMTransBasicConst::LTMFieldList01, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvListField01))));
        controlsInv.add(LTMTransBasicConst::LTMFieldList02, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvListField02))));
        controlsInv.add(LTMTransBasicConst::LTMFieldList03, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvListField03))));
        controlsInv.add(LTMTransBasicConst::LTMFieldList04, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvListField04))));
        controlsInv.add(LTMTransBasicConst::LTMFieldList05, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvListField05))));
        controlsInv.add(LTMTransBasicConst::LTMFieldList06, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvListField06))));
        controlsInv.add(LTMTransBasicConst::LTMFieldList07, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvListField07))));
        controlsInv.add(LTMTransBasicConst::LTMFieldList08, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvListField08))));
        controlsInv.add(LTMTransBasicConst::LTMFieldList09, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvListField09))));
        controlsInv.add(LTMTransBasicConst::LTMFieldList10, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvListField10))));
        controlsInv.add(LTMTransBasicConst::LTMInvEnablePS, formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvEnablePS))));
        controlsInv.add(LTMTransBasicConst::CountryRegionId,formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvCountryRegionId))));
        controlsInv.add(LTMTransBasicConst::StateId,formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvStateId))));
        controlsInv.add(LTMTransBasicConst::LTMStateDocTypeId,formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvStateDocTypeId))));
        controlsInv.add(LTMTransBasicConst::LTMStateDocNum,formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvStateDocNum))));
        controlsInv.add(LTMTransBasicConst::LTMTaxPayerTypeId,formRun.control(formRun.controlId(formControlStr(SalesEditLines, LTMInvTaxPayerTypeId))));

        // Dummy fields
        controlsInv.add(LTMTransBasicConst::BankGroupId,LTMInvBankGroupId);
        controlsInv.add(LTMTransBasicConst::LTMCPDSource,LTMInvCPDSource);
        controlsInv.add(LTMTransBasicConst::LTMControlCode,LTMInvControlCode);
        controlsInv.add(LTMTransBasicConst::LTMBeneficiary,LTMInvBeneficiary);
        controlsInv.add(LTMTransBasicConst::LTMHolderAccountNum,LTMInvHolderAccountNum);
        controlsInv.add(LTMTransBasicConst::LTMWithholdingBaseMST,LTMInvWithholdingBaseMST);
        controlsInv.add(LTMTransBasicConst::LTMWithholdAccruedAmountMST,LTMInvWithholdAccruedAmountMST);
        controlsInv.add(LTMTransBasicConst::LTMWithholdAccruedBaseMST,LTMInvWithholdAccruedBaseMST);
        controlsInv.add(LTMTransBasicConst::AmountMST,LTMInvAmountMST);
        controlsInv.add(LTMTransBasicConst::LTMWithholdingEffectiveRate,LTMInvWithholdingEffectiveRate);

        ltmSalesEditLines.parmControlsSalesEditLines(controlsPS, controlsInv);
        ltmSalesEditLines.init();
        ltmSalesEditLines.parmLTMReArrangeNow(false);
    }

Read CSV file using X++

class RGReadSample
{       
    ///

    /// Runs the class with the specified arguments.
    ///

    /// The specified arguments.
    public static void main(Args _args)
    {       
        AsciiStreamIo                                   file;
        Array                                           fileLines;
        FileUploadTemporaryStorageResult                fileUpload;
        fileUpload = File::GetFileFromUser() as FileUploadTemporaryStorageResult;
        file = AsciiStreamIo::constructForRead(fileUpload.openResult());
        if (file)
        {
            if (file.status())
            {
                throw error("@SYS52680");
            }
            file.inFieldDelimiter(',');
            file.inRecordDelimiter('\r\n');
        }
        container record;
        while (!file.status())
        {
            record = file.read();
            if (conLen(record))
            {
                info(strFmt("%1 - %2",conPeek(record,1),conPeek(record,2)));
            }
        }
        info("done");
    }
}

Extract files from zip file and upload using X++

 /// <summary>
    /// Extract files from zip file and upload.
    /// </summary>
    /// <param name = "_uploadFileURL">DownLoad of the zip file.</param>
    /// <param name = "_fileSuffixes>List of file suffixes which serves as filter.</param>
    /// <returns>List of file IDs of the unzipped files which meet filter in file suffix list; returns all files if the filter is empty.</returns>
    public static List getUnzippedFiles(SharedServiceUnitURL _uploadFileURL, container _fileSuffixes)
    {
        #File

        System.IO.Compression.ZipArchive zipArchive =
            new System.IO.Compression.ZipArchive(File::UseFileFromURL(_uploadFileURL), System.IO.Compression.ZipArchiveMode::Read);

        CLRObject archiveEntries =  zipArchive.get_Entries();
        int length = archiveEntries.get_Count();

        List uploadUrlList = new List(types::Container);
        container consUnzippedFile;
        int i;

        for (i = 0; i < length; i++)
        {
            System.IO.Compression.ZipArchiveEntry item = archiveEntries.get_Item(i);
            List fileNameComponents = strSplit(item.Name, #FileExtensionDelimiter);
            ListIterator iterator = new ListIterator(fileNameComponents);
            str fileSuffix;
            while (iterator.more())
            {
                fileSuffix = iterator.value();
                iterator.next();
            }

            if (conLen(_fileSuffixes) == 0 || conFind(_fileSuffixes, fileSuffix) > 0)
            {
                using(System.IO.MemoryStream stream = new System.IO.MemoryStream())
                {
                    item.Open().CopyTo(stream);
                    str fileID = BankStatementImportUtils::sendFileToTempStore(stream, item.Name).getFileId();
                    consUnzippedFile = [fileID, item.Name];
                    uploadUrlList.addEnd(consUnzippedFile);
                }
            }
        }

        return uploadUrlList;
    }

Sysoperation framework with file upload and UI builder D365FO

class ImportSveaFileService_Custom extends SysOperationServiceBase
{
    private Map companyLedgerJournalTableMap;
    public void run(ImportSveaFileContract_Custom _contract)
    {
        if (_contract.parmStorageResult() != conNull())
        {
            FileUploadTemporaryStorageResult fileUploadResult = new FileUploadTemporaryStorageResult();

            fileUploadResult.unpack(_contract.parmStorageResult());

            if (fileUploadResult != null)
            {
                try
                {
                    System.IO.Stream fileStream = File::UseFileFromURL(fileUploadResult.getDownloadUrl());

                    XmlDocument doc;
                    XmlNodeList data;
                    XmlElement nodeTable;
                    XmlElement nodeId;
                    XMLParseError xmlError;
                    doc = XmlDocument::newFromStream(fileStream);

                    int lineNum = 0;
                    real totalamountHeader, totalamountTrans = 0;
                    real totalDiscfee, totalAdmfee;
                    CurrencyCode currencyCode;

                    // Verify XML Document Structure
                    xmlError  = doc.parseError();
                    if(xmlError && xmlError.errorCode() != 0)
                    {
                        throw error(strFmt("XML Error: %1", xmlError.reason()));
                    }

                    BankAccountTable bankAccountTable = BankAccountTable::find(_contract.parmBankAccount());
                    LedgerJournalTable ledgerJournalTable = LedgerJournalTable::find(_contract.parmJournalId());
                    data = doc.selectNodes('//'+"header");
                    nodeTable = data.nextNode();
                    while (nodeTable)
                    {
                        nodeId = nodeTable.selectSingleNode("totalamount");
                        totalamountHeader = str2Num(nodeId.text());
                        nodeId = nodeTable.selectSingleNode("batchcurrency");
                        currencyCode = nodeId.text();
                      
                        nodeTable = data.nextNode();
                    }

                    data = doc.selectNodes('//'+"transaction");
                    nodeTable = data.nextNode();
                    //LedgerJournalTrans custLedgerJournalTrans;
                    //RecordInsertList   recordInsertListLedgerJournalTrans = new RecordInsertList(tableNum(LedgerJournalTrans));
                   
                    while (nodeTable)
                    {
                        ttsbegin;
                        real  discfee, admfee, invoiceamount;
                        LedgerJournalTrans custLedgerJournalTrans;

                        custLedgerJournalTrans.JournalNum   = ledgerJournalTable.JournalNum;
                        custLedgerJournalTrans.LineNum      = lineNum;

                        lineNum++;

                        Voucher     voucher;
                        NumberSeq   numberSeq;
                        numberSeq   = NumberSeq::newGetVoucherFromId(ledgerJournalTable.NumberSequenceTable);
                        voucher     = numberSeq.voucher();
                        custLedgerJournalTrans.Voucher          = voucher;

                        custLedgerJournalTrans.CurrencyCode     = currencyCode;
                        custLedgerJournalTrans.AccountType      = LedgerJournalACType::Cust;
                        custLedgerJournalTrans.TransactionType  = LedgerTransType::Payment;

                        nodeId = nodeTable.selectSingleNode("invoicedate");
                        custLedgerJournalTrans.TransDate = str2Date(nodeId.text(),321);
                     
                        nodeId = nodeTable.selectSingleNode("customernumber");
                        CustAccount custAccountNum = CustTable::findRecId(str2recId(nodeId.text())).AccountNum;
                        custLedgerJournalTrans.parmAccount(custAccountNum, LedgerJournalACType::Cust);
                    
                        CustTable custTable = CustTable::find(custLedgerJournalTrans.parmAccount());

                        if (custTable)
                        {
                            custLedgerJournalTrans.DefaultDimension = custTable.DefaultDimension;
                            custLedgerJournalTrans.Payment = custTable.PaymTermId;
                        }
             
                        nodeId = nodeTable.selectSingleNode("invoiceid");
                        custLedgerJournalTrans.MarkedInvoice        = nodeId.text();
                        custLedgerJournalTrans.MarkedInvoiceCompany = curExt();

                        nodeId = nodeTable.selectSingleNode("invoiceamount");
                        invoiceamount = str2Num(nodeId.text());
                        custLedgerJournalTrans.amountCur2DebCred(invoiceamount * -1);

                        custLedgerJournalTrans.OffsetAccountType            = LedgerJournalACType::Bank;
                        custLedgerJournalTrans.OffsetLedgerDimension        = bankAccountTable.LedgerDimension;
                        custLedgerJournalTrans.Triangulation                = Currency::triangulation(custLedgerJournalTrans.CurrencyCode, custLedgerJournalTrans.TransDate);
                        custLedgerJournalTrans.ExchRate                     = ExchangeRateHelper::exchRate(custLedgerJournalTrans.CurrencyCode, custLedgerJournalTrans.TransDate);
                        custLedgerJournalTrans.ExchRateSecond               = ExchangeRateHelper::exchRateSecond(custLedgerJournalTrans.CurrencyCode, custLedgerJournalTrans.TransDate);
                        custLedgerJournalTrans.PostingProfile               = CustParameters::find().PostingProfile;
                        custLedgerJournalTrans.BankCentralBankPurposeCode   = custTable.BankCentralBankPurposeCode;
                        custLedgerJournalTrans.BankCentralBankPurposeText   = custTable.BankCentralBankPurposeText;
                        custLedgerJournalTrans.Txt                          = this.determineTransactionText(LedgerTransTxt::CustVendNetVendor, custLedgerJournalTrans);
                        custLedgerJournalTrans.OffsetTxt                    = this.determineTransactionText(LedgerTransTxt::CustVendNetLedger, custLedgerJournalTrans);

                        nodeId  = nodeTable.selectSingleNode("discfee");
                        discfee = str2Num(nodeId.text());

                        nodeId  = nodeTable.selectSingleNode("admfee");
                        admfee   = str2Num(nodeId.text());

                        CustInvoiceJour CustInvoiceJour;
                       
                        select firstonly custInvoiceJour
                            where custInvoiceJour.InvoiceId == custLedgerJournalTrans.MarkedInvoice &&
                                    custInvoiceJour.InvoiceDate == custLedgerJournalTrans.TransDate &&
                                        custInvoiceJour.InvoiceAccount == custTable.AccountNum;

                        if (invoiceamount != custInvoiceJour.InvoiceAmount)
                        {
                            warning(strFmt("Invoice amount mismatch on %1", custInvoiceJour.InvoiceId));
                        }

                        totalamountTrans += invoiceamount;
                        totalDiscfee     += discfee;
                        totalAdmfee      += admfee;

                        nodeTable = data.nextNode();

                        custLedgerJournalTrans.insert();
                        
                        LedgerJournalEngine_CustPayment::updateMarkedInvoiceSpecTrans(custLedgerJournalTrans);
                        ttscommit;
                        //recordInsertListLedgerJournalTrans.add(custLedgerJournalTrans);
                    }
                   // recordInsertListLedgerJournalTrans.insertDatabase();
                    info(strFmt("Total amount from file: %1 - total from journal: %2 -Total discfee: %3 – Total admfee: %4",totalamountHeader, totalamountTrans, totalDiscfee, totalAdmfee));
                }
                catch (Exception::Error)
                {
                    error("@RET433");
                }
            }
        }
    }

    private TransactionTextLarge determineTransactionText(
        LedgerTransTxt _ledgerTransTxt,
        LedgerJournalTrans _ledgerJournalTrans)
    {
        TransactionTxt transactionTxt = TransactionTxt::construct();

        transactionTxt.setType(_ledgerTransTxt);
        transactionTxt.setVoucher(_ledgerJournalTrans.Voucher);
        transactionTxt.setDate(_ledgerJournalTrans.TransDate);
        transactionTxt.setKey1(_ledgerJournalTrans.parmAccount());
        transactionTxt.setFormLetter(_ledgerJournalTrans.Voucher);

        return transactionTxt.txt();
    }

}
===========
class ImportSveaFileUIBuilder_Custom extends SysOperationAutomaticUIBuilder
{
    private str                 availableTypes = ".xml";
    private const str           OkButtonName = 'CommandButton';
    private const str           FileUploadName = 'FileUpload';

    ImportSveaFileContract_Custom   contract;
    /// <summary>
    /// Overriden the <c>postBuild</c> method to add a <c>FileUpload</c> control
    /// </summary>
    public void postBuild()
    {
        DialogGroup      dialogGroup;
        FormBuildControl formBuildControl;
        FileUploadBuild  dialogFileUpload;

        super();

        contract = this.dataContractObject();
        
        dialogGroup = dialog.addGroup("File path");
        formBuildControl = dialog.formBuildDesign().control(dialogGroup.name());
       
        dialogFileUpload = formBuildControl.addControlEx(classstr(FileUpload), FileUploadName);
        dialogFileUpload.style(FileUploadStyle::MinimalWithFilename);
        dialogFileUpload.baseFileUploadStrategyClassName(classstr(FileUploadTemporaryStorageStrategy));
        dialogFileUpload.fileTypesAccepted(availableTypes);
        dialogFileUpload.fileNameLabel("@SYS308842");
    }

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

    /// <summary>
    /// Executes logic for unsubscribing the registered events on the form
    /// </summary>
    /// <param name = "sender"></param>
    /// <param name = "e"></param>
    [SuppressBPWarningAttribute('BPParameterNotUsed', 'This is event parameter not required to use')]
    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 fileUpload = _formRun.control(_formRun.controlId(FileUploadName));
        fileUpload.notifyUploadCompleted -= eventhandler(this.uploadCompleted);
        fileUpload.notifyUploadAttemptStarted -= eventhandler(this.uploadStarted);
        _formRun.onClosing -= eventhandler(this.dialogClosing);
    }

    /// <summary>
    /// Executes additional logic once the upload of the file is completed
    /// </summary>
    protected void uploadCompleted()
    {
        var formRun = this.dialog().dialogForm().formRun();
        FileUpload fileUpload = formRun.control(formRun.controlId(FileUploadName));
        FileUploadTemporaryStorageResult uploadResult = fileUpload.getFileUploadResult();

        if (uploadResult != null && uploadResult.getUploadStatus())
        {
            contract.parmStorageResult(uploadResult.pack());
        }

        this.setDialogOkButtonEnabled(formRun, true);
    }

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

    /// <summary>
    /// Enables/Disables the OK button of the dialog
    /// </summary>
    /// <param name = "_formRun">The instance of the dialog form</param>
    /// <param name = "_isEnabled">Should the OK button be enabled?</param>
    protected void setDialogOkButtonEnabled(FormRun _formRun, boolean _isEnabled)
    {
        FormControl okButtonControl = _formRun.control(_formRun.controlId(OkButtonName));
        if (okButtonControl)
        {
            okButtonControl.enabled(_isEnabled);
        }
    }

    /// <summary>
    /// Override of the <c>postRun</c> method in order to add events subscriptions
    /// </summary>
    public void postRun()
    {
        super();

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

        this.setDialogOkButtonEnabled(formRun, false);
    }

}
===
class ImportSveaFileController_Custom extends SysOperationServiceController
{
    /// <summary>
    ///     Constructs an instance
    /// </summary>
    /// <returns>an instance of class</returns>
    public static ImportSveaFileController_Custom construct(Args _args)
    {
        ImportSveaFileController_Custom controller = new ImportSveaFileController_Custom(classstr(ImportSveaFileService_Custom),
                                                                                           methodstr(ImportSveaFileService_Custom, run),
                                                                                           SysOperationExecutionMode::Synchronous);
        controller.parmArgs(_args);
        controller.initializeContract(controller);

        return controller;
    }

    /// <summary>
    /// Initializes the contract parameters.
    /// </summary>
    /// <param name = "_controller">An instance of the <c>CAMPeriodCalculationController</c> whose contract to initialize.</param>
    protected void initializeContract(ImportSveaFileController_Custom _controller)
    {
        LedgerJournalTable ledgerJournalTable = this.parmArgs().record();

        if (ledgerJournalTable)
        {
            ImportSveaFileContract_Custom contract = _controller.getDataContractObject() as ImportSveaFileContract_Custom;

            if (contract)
            {
                contract.parmJournalId(ledgerJournalTable.JournalNum);
            }
        }
    }

    /// <summary>
    /// Instantiate controller.
    /// </summary>
    //protected void new()
    //{
    //    super(classStr(ImportSveaFileService_Custom),
    //    methodStr(ImportSveaFileService_Custom,run ));
    //    this.parmDialogCaption("Import Svea file into payment journal");
    //}

    /// <summary>
    /// Runs the class with the specified arguments.
    /// </summary>
    /// <param name = "_args">The specified arguments.</param>
    public static void main(Args _args)
    {
        ImportSveaFileController_Custom::construct(_args).startOperation();
        //ImportSveaFileController_Custom controller = ImportSveaFileController_Custom::newFromArgs();
        //controller.parmExecutionMode(SysOperationExecutionMode::Synchronous);
        //controller.startOperation();
    }

    /// <summary>
    /// Instantiate and initialize controller class.
    /// </summary>
    /// <returns>
    /// returns controller class.
    /// </returns>
    //public static ImportSveaFileController_Custom newFromArgs()
    //{
    //    ImportSveaFileController_Custom controller = ImportSveaFileController_Custom::construct();
    //    return controller;
    //}

}
====
[DataContractAttribute,
SysOperationContractProcessing(classStr(ImportSveaFileUIBuilder_Custom))]
class ImportSveaFileContract_Custom
{
    LedgerJournalId   journalId;
    CompanyBankAccountId bankAccount;
    container       storageResult;

    /// <summary>
    ///     Get/Set the email address
    /// </summary>
    /// <param name = "_emailAddress">email address</param>
    /// <returns>email address</returns>
    [DataMemberAttribute,
    SysOperationLabelAttribute(literalStr("Bank account")),
    SysOperationDisplayOrderAttribute('1')]
    public CompanyBankAccountId parmBankAccount(CompanyBankAccountId _bankAccount = bankAccount)
    {
        bankAccount = _bankAccount;

        return bankAccount;
    }

    /// <summary>
    ///     Get/Set the email address
    /// </summary>
    /// <param name = "_emailAddress">email address</param>
    /// <returns>email address</returns>
    [DataMemberAttribute,
    SysOperationLabelAttribute(literalStr("Journal num")),
    SysOperationDisplayOrderAttribute('3'),
        SysOperationControlVisibility(false)]
    public LedgerJournalId parmJournalId(LedgerJournalId _journalId = journalId)
    {
        journalId = _journalId;

        return journalId;
    }

    /// <summary>
    /// Parameter method which holds values of the packed variables from <c>FileUploadTemporaryStorageResult</c> class
    /// </summary>
    /// <param name = "_storageResult">Packed instance of <c>FileUploadTemporaryStorageResult</c> class</param>
    /// <returns>Container with packed values</returns>
    [DataMemberAttribute('StorageResult'),
        SysOperationDisplayOrderAttribute('2')]
    public container parmStorageResult(container _storageResult =  storageResult)
    {
        storageResult = _storageResult;
        return storageResult;
    }

}

Write IOStream to azure blob using X++

/// <summary>
///     Service class for export customer details to blob storage
///    /// </summary>
using BlobStorage = Microsoft.WindowsAzure.Storage;
using Microsoft.Dynamics.AX.Framework.FileManagement;
using System.IO;
class ExportCustomerDetailsToAzureBlobService extends SysOperationServiceBase
{
    QueryRun    queryRun;
    CustTable   custTable;
 
    /// <summary>
    ///     Export customer details to blob storage
    /// </summary>
    /// <param name = "_contract">contract class</param>
    public void run(ExportCustomerDetailsToAzureBlobContract _contract)
    {
        // create a new queryrun object
        queryRun = new queryRun(_contract.getQuery());
 
        CommaStreamIo       io = CommaStreamIo::constructForWrite();
        const str           connectionString = "DefaultEndpointsProtocol=https;AccountName=arorasamplestorageacc;AccountKey=qRAbReWrFS5a4pVg7fVBdACk0/q4AzpwM6YBT7LYT4oAapZjCr1Wc5bNB1Lia9oYZXO7R/M17okx+AStaDNKFg==;EndpointSuffix=core.windows.net";
        const str           containerName = "arorasamplecontainer";
 
        TransDate   curDate = DateTimeUtil::getToday(DateTimeUtil::getUserPreferredTimeZone());
 
        str         curYear = int2Str(year(curDate));
        str         curMth  = int2Str(mthOfYr(curDate));
 
        if (strLen(curMth) == 1)
        {
            curMth = '0'+curMth;
        }
 
        str         curDay = int2Str(dayOfMth(curDate));
 
        if (strLen(curDay) == 1)
        {
            curDay = '0'+curDay;
        }
 
        str         mergedDate = curYear+curMth+curDay;
 
        TimeOfDay  curTime = DateTimeUtil::getTimeNow(DateTimeUtil::getUserPreferredTimeZone());
 
        str         mergedTime = time2StrHMS(curTime);
        mergedTime = strRem(mergedTime, ':');
        mergedTime = strRem(mergedTime, ' ');
 
        if (strLen(mergedTime) == 5)
        {
            mergedTime = '0'+mergedTime;
        }
 
        str         mergedDateTime = mergedDate+mergedTime;
 
        str         fileName = strFmt('%1_%2.csv',curExt(), mergedDateTime);
        BlobStorage.CloudStorageAccount storageAccount = BlobStorage.CloudStorageAccount::Parse(connectionString);
        BlobStorage.Blob.CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
        BlobStorage.Blob.CloudBlobContainer blobContainer = blobClient.GetContainerReference(containerName);
        blobContainer.CreateIfNotExistsAsync();
        io.writeExp(['Data area', 'Org number' , 'Tax receipt number']);
        // loop all results from the query
        while(queryRun.next())
        {
            custTable = queryRun.get(tablenum(CustTable));
 
            DirOrganization organization = DirOrganization::find(custTable.Party);
 
            io.writeExp([custTable.DataAreaId, organization.OrgNumber, subStr(custTable.VATNum, 2, -3)]);
        }
        BlobStorage.Blob.CloudBlockBlob blockBlob = blobContainer.GetBlockBlobReference(fileName);
        if(blockBlob && !blockBlob.Exists(null,null))
        {
            System.IO.Stream stream = iO.getStream();
            stream.Position = 0;
            System.IO.StreamReader reader = new System.IO.StreamReader(stream);
            str csvFileContent = reader.ReadToEnd();
            if(csvFileContent)
            {
                blockBlob.UploadText(csvFileContent,null,null,null,null);
                blockBlob.FetchAttributes(null,null,null);
                BlobStorage.Blob.BlobProperties blobProperties = blockBlob.Properties;
                if(blobProperties.Length != 0)
                {
                    info('File upload successful');
                }
            }
        }
        else
        {
            info('File already exits');
        }
    }
 
}

use AOTResource images as container X++

container image = ImageReference::constructForAotResource(resourceStr("RedStatus")).pack();

Send email X++

public static void sendEmail(str _fileName,
                                 str _subject,
                                 str _body,
                                 System.IO.MemoryStream _memoryStream,
                                 Email _fromEmail, 
                                 Email _toEmail)
    {
        SysMailerMessageBuilder mailer = new SysMailerMessageBuilder();
        try
        {
            mailer.setSubject(_subject);

            //mailer.setFrom(_fromEmail);

            mailer.setBody(_body);

            mailer.addTo(_toEmail);

            mailer.addAttachment(_memoryStream, _fileName);

            SysMailerFactory::sendNonInteractive(mailer.getMessage());

            Info("@SYS58551");
        }
        catch (Exception::CLRError)
        {
            System.Exception ex = ClrInterop::getLastException();
            if (ex != null)
            {
                ex = ex.get_InnerException();
                if (ex != null)
                {
                    error(ex.ToString());
                }
            }
        }
        catch (Exception::Error)
        {
            Error("@ErrorOccurredFailedSendEmail");
        }
    }

Table browser URL in D365FO

Critical Thinking icon icon by Icons8