Thursday, 3 July 2025

Bank statement import through batch job using blob folder in d365 FO X++

Action menu items: HMT_BankStatementImportSysOperationController

Label: @HMT:HMT_ProcessBankImportStatement

Help text: @HMT:HMT_ProcessBankImportStatement

object type: Class

Object: HMT_BankStatementImportSysOperationController

---

Class:

/// <summary>

/// Import class that is used in SysOp

/// </summary>

using Microsoft.Dynamics365.LocalizationFramework;

using Microsoft.Azure;

using Microsoft.WindowsAzure;

using Microsoft.WindowsAzure.StorageClient;

using System.Collections.Generic;

class HMT_BankStatementImportProcess

{

    protected static BankStatementFormatRefRecId bankStatementFormatRecId;

    protected SharedServiceUnitFileID uploadFileID;

    protected Filename uploadFileName;

    protected str uploadFileURL;

    private const str ZipFileSuffix = 'zip';

    private const str comma = ",";

    private int successfulStatementNumber;

    private int totalStatementNumber;

    private List executions;

    protected CompanyBankAccountId bankAccount;

    protected static BankReconciliationMatchAutoAfterImport matchAutoAfterImport;

    public static FileUploadTemporaryStorageResult storageResult2;

    //public static ClassName callerclassname;

    public static Description1000  fileValues2;

    public static container        fileValuesCon2;

    boolean isImported;

    /// <summary>

    /// Import bank statements.

    /// </summary>

    /// <returns>List containing imported bank account records and a related list of bank statements.</returns>

    /// <remarks>

    /// A single file can be a zip file that contains multiple files or just a single file. Then, each file can contain multiple statements and each of

    /// those statements can belong to a different bank account and company.

    /// </remarks>

    protected List importBankStatements()

    {

        List importedBankAccounts;

 

        BankStatementFormat format = BankStatementFormat::find(bankStatementFormatRecId);

        if (fileValues2)

        {

            fileValuesCon2           = str2con(fileValues2, comma);

            uploadFileURL            = conPeek(fileValuesCon2, 1);

            uploadFileName           = conPeek(fileValuesCon2, 2);

            uploadFileID             = conPeek(fileValuesCon2, 3);

        }

        List bankFiles = this.findBankFiles();

        ListEnumerator bankFileEnumerators = bankFiles.getEnumerator();

        while (bankFileEnumerators.moveNext())

        {

            if (format.UseGERConfiguration)

            {

                SharedServiceUnitFileID bankStatementToImportFileId = conPeek(bankFileEnumerators.current(), 1);

                importedBankAccounts = this.doImportBankStatementsGER(bankStatementToImportFileId, format.ERModelMappingTable);

            }


        }


        return importedBankAccounts;

    }


    /// <summary>

    /// Find bank files

    /// </summary>

    /// <returns>List</returns>

    private List findBankFiles()

    {

        #Characters

        List bankFiles;


        if (strEndsWith(uploadFileName, ZipFileSuffix))

        {

            container fileSuffixes= conNull();


            List fileTypesList = strSplit(BankStatementFormat::find(bankStatementFormatRecId).FilenameType, #semicolon);

            ListEnumerator enumerator = fileTypesList.getEnumerator();

            while (enumerator.moveNext())

            {

                fileSuffixes += enumerator.current();

            }


            bankFiles = DataFileImportExportUtils_W::getUnzippedFiles(uploadFileURL, fileSuffixes, classStr(BankStatementFileTemporaryStorageStrategy));


            this.deleteFile(uploadFileID);

        }

        else

        {

            bankFiles = new List(Types::Container);

            bankFiles.addEnd([uploadFileID, uploadFileName]);

        }


        return bankFiles;

    }


    /// <summary>

    /// DeleteFile

    /// </summary>

    /// <param name = "_fileId">SharedServiceUnitFileID</param>

    private void deleteFile(SharedServiceUnitFileID _fileId)

    {

        FileUploadTemporaryStorageResult result = new FileUploadTemporaryStorageResult('', '', true, '', '', _fileId);

        result.deleteResult();

    }


    /// <summary>

    /// Performs import of bank statement file by using GER.

    /// </summary>

    /// <param name = "_bankStatementToImportFileId">The Field Id of the file to be imported.</param>

    /// <param name = "_erModelMappingId">The identification of model mapping to be used for import.</param>

    /// <returns>

    /// List of imported bank statement IDs.

    /// </returns>

    private List doImportBankStatementsGER(

        SharedServiceUnitFileID _bankStatementToImportFileId,

        ERModelMappingId _erModelMappingId)

    {

        List importedBankAccounts = new List(Types::Class);


        Name gerConfigName = ERModelMappingTable::find(_erModelMappingId).Name;

        Description executionID = DMFUtil::generateExecutionId(gerConfigName);


        this.gerImportStatement(_bankStatementToImportFileId, _erModelMappingId, gerConfigName, executionID);


        try

        {

            this.reverseDebitCreditForBankStatement(gerConfigName, executionID);

        }

        catch

        {

            this.deleteBankStmtsImported(gerConfigName, executionID);

            error(strFmt("@CashManagement:BankStmtImportFailReverseDebitCreditFail", uploadFileName));

        }


        BankStatementDocumentStaging bankStatementDocumentStaging;

        BankStmtISODocument bankStmtISODocument;

        BankStmtISOAccountStatement bankStmtISOAccountStatement;


        BankStatementImportData importData;


        while select crosscompany bankStmtISOAccountStatement group by DataAreaId, BankAccountTable, Identification

        exists join bankStmtISODocument

            where bankStmtISODocument.RecId == bankStmtISOAccountStatement.BankStmtISODocument

        exists join bankStatementDocumentStaging

            where bankStatementDocumentStaging.BankStatementDocumentRecId == bankStmtISODocument.RecId

                && bankStatementDocumentStaging.DefinitionGroup == gerConfigName

                && bankStatementDocumentStaging.ExecutionId == executionID

        {

            if (!importData

                || importData.parmBankAccountTable().DataAreaId != bankStmtISOAccountStatement.DataAreaId

                || importData.parmBankAccountTable().AccountID != bankStmtISOAccountStatement.BankAccountTable)

            {

                importData = new BankStatementImportData();

                importData.parmBankAccountTable(BankAccountTable::findByCompany(

                    bankStmtISOAccountStatement.DataAreaId,

                    bankStmtISOAccountStatement.BankAccountTable));

                importData.parmImportedStatements(new List(Types::String));

                importedBankAccounts.addEnd(importData);

            }


            importData.parmImportedStatements().addEnd(bankStmtISOAccountStatement.Identification);

        }


        select crosscompany count(RecId) from bankStmtISOAccountStatement

        exists join bankStmtISODocument

            where bankStmtISODocument.RecId == bankStmtISOAccountStatement.BankStmtISODocument

        exists join bankStatementDocumentStaging

            where bankStatementDocumentStaging.BankStatementDocumentRecId == bankStmtISODocument.RecId

                && bankStatementDocumentStaging.DefinitionGroup == gerConfigName

                && bankStatementDocumentStaging.ExecutionId == executionID;


        successfulStatementNumber = int642int(bankStmtISOAccountStatement.RecId);

        totalStatementNumber = int642int(bankStmtISOAccountStatement.RecId);

        if(successfulStatementNumber==0)

        {

            select crosscompany count(RecId) from bankStmtISOAccountStatement

            exists join bankStmtISODocument

                where bankStmtISODocument.RecId == bankStmtISOAccountStatement.BankStmtISODocument;

            if(bankStmtISODocument.RecId > 0)

            {

                successfulStatementNumber = int642int(bankStmtISODocument.RecId);

                totalStatementNumber      = int642int(bankStmtISOAccountStatement.RecId);

                info(strFmt("@CashManagement:BankStatementImportCount", successfulStatementNumber));

            }

        }



        delete_from bankStatementDocumentStaging

            where bankStatementDocumentStaging.DefinitionGroup == gerConfigName

                && bankStatementDocumentStaging.ExecutionId == executionID;


        if (_bankStatementToImportFileId != '')

        {

            this.deleteFile(_bankStatementToImportFileId);

        }


        return importedBankAccounts;

    }


    //Started adding


    /// <summary>

    /// Delete all related bank statement data which is imported before. If the statement status is not `Open`, then it can not be deleted.

    /// </summary>

    /// <param name = "_gerConfigName">GER configuration name</param>

    /// <param name = "_executionID">Execution ID</param>

    public void deleteBankStmtsImported(Name _gerConfigName, Description _executionID)

    {

        BankStatementDocumentStaging bankStatementDocumentStaging;

        BankStmtISODocument bankStmtISODocument;

        BankStmtISOAccountStatement bankStmtISOAccountStatement;


        while select forupdate crosscompany bankStmtISOAccountStatement

            where bankStmtISOAccountStatement.Status == BankStatementStatus::Open

        exists join bankStmtISODocument

            where bankStmtISOAccountStatement.BankStmtISODocument == bankStmtISODocument.RecId

        exists join bankStatementDocumentStaging

            where bankStatementDocumentStaging.BankStatementDocumentRecId == bankStmtISODocument.RecId

                && bankStatementDocumentStaging.DefinitionGroup == _gerConfigName

                && bankStatementDocumentStaging.ExecutionId == _executionID

        {

            changecompany(bankStmtISOAccountStatement.DataAreaId)

            {

                bankStmtISOAccountStatement.delete();

            }

        }

    }


     public void reverseDebitCreditForBankStatement(Name _gerConfigName, Description _executionID)

    {

        BankStatementDocumentStaging bankStatementDocumentStaging;

        BankStmtISODocument bankStmtISODocument;

        BankStmtISOAccountStatement bankStmtISOAccountStatement;

        BankAccountTable bankAccountTable;


        ttsbegin;


        while select crosscompany bankStmtISOAccountStatement

            where bankStmtISOAccountStatement.Status == BankStatementStatus::Open

        join bankAccountTable

            where bankStmtISOAccountStatement.DataAreaId == bankAccountTable.DataAreaId

                && bankStmtISOAccountStatement.BankAccountTable == bankAccountTable.AccountID

                && bankAccountTable.ReverseDebitCredit == NoYes::Yes

        exists join bankStmtISODocument

            where bankStmtISODocument.RecId == bankStmtISOAccountStatement.BankStmtISODocument

        exists join bankStatementDocumentStaging

            where bankStatementDocumentStaging.BankStatementDocumentRecId == bankStmtISODocument.RecId

                && bankStatementDocumentStaging.DefinitionGroup == _gerConfigName

                && bankStatementDocumentStaging.ExecutionId == _executionID

        {

            changecompany(bankStmtISOAccountStatement.DataAreaId)

            {

                this.reverseDebitCreditInBankStmtISOStatementLine(bankStmtISOAccountStatement.RecId);

                this.reverseDebitCreditInBankStmtISOCashBalance(bankStmtISOAccountStatement.RecId);

            }

        }


        ttscommit;

    }


    public void reverseDebitCreditInBankStmtISOStatementLine(RecId _bankStmtISOAccountStatementRecId)

    {

        BankStmtISOReportEntry bankStmtISOReportEntry;

        bankStmtISOReportEntry.skipDataMethods(true);

        bankStmtISOReportEntry.skipEvents(true);


        update_recordset bankStmtISOReportEntry

        setting AmountCreditDebitIndicator = !bankStmtISOReportEntry.AmountCreditDebitIndicator

            where bankStmtISOReportEntry.BankStmtISOAccountStatement == _bankStmtISOAccountStatementRecId;

    }


    public void reverseDebitCreditInBankStmtISOCashBalance(RecId _bankStmtISOAccountStatementRecId)

    {

        BankStmtISOCashBalance bankStmtISOCashBalance;

        bankStmtISOCashBalance.skipDataMethods(true);

        bankStmtISOCashBalance.skipEvents(true);


        update_recordset bankStmtISOCashBalance

        setting AmountCreditDebitIndicator = !bankStmtISOCashBalance.AmountCreditDebitIndicator

            where bankStmtISOCashBalance.BankStmtISOAccountStatement == _bankStmtISOAccountStatementRecId;

    }


    //End


    /// <summary>

    /// Imports bank statements from the file by using GER importing configuration.

    /// </summary>

    /// <param name = "_uploadedStatement">The Field Id of the file to be imported.</param>

    /// <param name = "_erModelMappingId">GER model mapping format.</param>

    /// <param name = "_gerConfigName">The name of GER configuration.</param>

    /// <param name = "_executionID">Unique execution Id wich is used to identify the imported bank document.</param>

    protected void gerImportStatement(

        SharedServiceUnitFileID _uploadedStatement,

        ERModelMappingId _erModelMappingId,

        Name _gerConfigName,

        Description _executionID)

    {

        var integrationPoint = classStr(ERTableDestination) + '#' + tableStr(BankStatementDocumentEntity);

        ERmodelDefinitionInputParametersAction inputParameters = new ERmodelDefinitionInputParametersAction();

        inputParameters.addParameter('$ExecutionID', _executionID)

            .addParameter('$gerConfigName', _gerConfigName)

            .addParameter('$AccountId', bankAccount);


        var runner = ERObjectsFactory::createMappingDestinationRunByImportFormatMappingId(_erModelMappingId, integrationPoint);

        runner.withParameter(inputParameters);

        runner.init();

        

        if (runner.promptsContractedModelMapping())

        {

            var parameters = runner.getParameters();

            var traverser = new ERModelDefinitionParametersTraverser(parameters);

            while (traverser.moveNext())

            {

                ERIImportFormatDataSourceContract current = ERCast::asObject(traverser.current()) as ERIImportFormatDataSourceContract;

                if (current)

                {

                    current.parmInputDataStream(File::UseFileFromURL(DMFStagingWriter::getDownloadURLFromFileId(_uploadedStatement)));

                }

            }

        }


        ERIModelMappingRunResult modelMappingRunResult = runner.runUnattendedAndReturnResult();

        HMT_BankStatementImportProcess::processModelMappingRunResult(modelMappingRunResult);


        int validationErrors = modelMappingRunResult.get_ValidationErrors().Count;

        if(validationErrors > 0)

        {

            isImported = false;

        }

        else

        {

            isImported = modelMappingRunResult.IsSuccess();

        }



    }


    private static void processModelMappingRunResult(ERIModelMappingRunResult _modelMappingRunResult = null)

    {

        if (_modelMappingRunResult && !_modelMappingRunResult.IsSuccess() && !_modelMappingRunResult.get_IsExceptionCalledByValidation())

        {

            HMT_ERExceptionUtils::processFailedRunExceptionsToInfolog(_modelMappingRunResult, "@ElectronicReporting:ModelMapping");

        }

        else if (_modelMappingRunResult && !_modelMappingRunResult.IsSuccess() && _modelMappingRunResult.get_IsExceptionCalledByValidation())

        {

            throw error(strFmt("@ElectronicReporting:ValidationErrorsDuringRun", "@ElectronicReporting:ModelMapping"));

        }

    }


    private boolean isRunSuccessful(ERIRunResult _runResult)

    {

        return _runResult ? _runResult.IsSuccess() : false;

    }


    /// <summary>

    /// RunBankStatementimport

    /// </summary>

    public void runBankStatementImport()

    {

        executions = new List(Types::Record);


        BankParameters bankParameters = BankParameters::find();

        try

        {

            List importedBankAccounts = this.importBankStatements();


            if (matchAutoAfterImport

            && importedBankAccounts.elements())

            {

                BankStatementImportAutoMatchHelper autoMatchHelper = new BankStatementImportAutoMatchHelper();

                autoMatchHelper.processAutomaticMatch(importedBankAccounts);

            }


            if (totalStatementNumber == 0 && successfulStatementNumber == 0

            && BankStatementFormat::find(bankStatementFormatRecId).UseGERConfiguration == NoYes::No)

            {

                info("@CashManagement:ImportStatementNoImportsSuccessful");

            }


            if (successfulStatementNumber > 0)

            {

                HMT_BlobFileHelper::deleteBankStatementAndTransfer(bankParameters.BlobAccountName, bankParameters.BlobPublicKey, bankParameters.BlobReference, storageResult2.getFileName(), bankParameters.BlobSuccessful);

                isImported = true;

                info(strFmt("@HMTships:HMT_BankStatementImportSuccess", storageResult2.getFileName()));

                info(strFmt("@CashManagement:BankStatementImportCount", successfulStatementNumber));

            }


            if (totalStatementNumber - successfulStatementNumber > 0)

            {

                info(strFmt("@CashManagement:BankStatementImportFailCount", totalStatementNumber - successfulStatementNumber));

            }


            //Failed import - transfer file to Failed blob container

            if(!isImported)

            {

                HMT_BlobFileHelper::deleteBankStatementAndTransfer(bankParameters.BlobAccountName, bankParameters.BlobPublicKey, bankParameters.BlobReference, storageResult2.getFileName(), bankParameters.BlobFailed);

                warning(strFmt("@HMTships:HMT_BankStatementImportFailed", storageResult2.getFileName()));

            }

            else if(successfulStatementNumber == 0 && isImported == true)

            {

                HMT_BlobFileHelper::deleteBankStatementAndTransfer(bankParameters.BlobAccountName, bankParameters.BlobPublicKey, bankParameters.BlobReference, storageResult2.getFileName(), bankParameters.BlobSuccessful);

                info(strFmt("@HMTships:HMT_BankStatementImportSuccess", storageResult2.getFileName()));

            }


            this.reportErrors();

        }

        catch

        {

            //Failed import - transfer file to Failed blob container

            if(!isImported)

            {

                HMT_BlobFileHelper::deleteBankStatementAndTransfer(bankParameters.BlobAccountName, bankParameters.BlobPublicKey, bankParameters.BlobReference, storageResult2.getFileName(), bankParameters.BlobFailed);

                warning(strFmt("@HMTships:HMT_BankStatementImportFailed", storageResult2.getFileName()));

            }

        }

    }


    /// <summary>

    /// Report errors

    /// </summary>

    private void reportErrors()

    {

        ListEnumerator listEnumerator = executions.getEnumerator();

        while (listEnumerator.moveNext())

        {

            DMFDefinitionGroupExecution execution = listEnumerator.current();


            DMFStagingValidationLog log;

            while select log

                where log.ExecutionId == execution.ExecutionId

            {

                checkFailed(log.ErrorMessage);

            }

        }

    }


    /// <summary>

    /// Entry point of the service class

    /// </summary>

    /// <param name = "_bankStatementFormatRefRecId">BankStatementFormatRefRecId</param>

    /// <param name = "_matchAutoAfterImport">BankReconciliationMatchAutoAfterImport</param>

    public void runProcess(BankStatementFormatRefRecId _bankStatementFormatRefRecId, BankReconciliationMatchAutoAfterImport _matchAutoAfterImport)

    {

        HMT_BankStatementImportProcess    import;

        import                                 = HMT_BankStatementImportProcess::construct();

        List           filenames               = new List(Types::String);

        ListIterator   it;

        BankParameters bankParameters          = BankParameters::find();

        bankStatementFormatRecId               = _bankStatementFormatRefRecId;

        matchAutoAfterImport                   = _matchAutoAfterImport;

        string50       azureBlobName           = bankParameters.BlobAccountName;

        str            azureBlobAccessKey      = bankParameters.BlobPublicKey;

        str            azureContainerReference = bankParameters.BlobReference;

        filenames = HMT_BlobFileHelper::getFileNamesFromBlob(azureBlobName, azureBlobAccessKey, azureContainerReference);

        it        = new ListIterator(filenames);

        while (it.more())

        {

            str fileNameValue = it.value();

            storageResult2 = HMT_BankStatementImportProcess::getBankStatementFilesFromBlob(fileNameValue, azureContainerReference, azureBlobName, azureBlobAccessKey);

            fileValues2               = strFmt("@HMTships:HMT_FileValuesFormat",

                                        storageResult2.getDownloadUrl(),

                                        storageResult2.getFileName(),

                                        storageResult2.getFileId());

            import.runBankStatementImport();

            

            it.next();

        }

    }


    /// <summary>

    /// getBankStatementFilesFromBlob

    /// </summary>

    /// <param name = "_fileName">File name of the bank statement</param>

    /// <param name = "_blobReference">Source blob container</param>

    /// <param name = "_blobAccountName">Storage account name</param>

    /// <param name = "_blobPublicKey">Access key</param>

    /// <returns>FileUploadTemporaryStorageResult</returns>

    public static FileUploadTemporaryStorageResult getBankStatementFilesFromBlob(str _fileName, str _blobReference, str _blobAccountName, str _blobPublicKey)

    {

        ttsbegin;

        BankParameters                   parametersInterfaces  = BankParameters::find();

        System.IO.MemoryStream                    memStream    = new  System.IO.MemoryStream();

        FolderName                              containerName  = _blobReference;

        Filename                                     filename  = _fileName;


        StorageCredentialsAccountAndKey          credentials   = new StorageCredentialsAccountAndKey(_blobAccountName, _blobPublicKey);

        CloudStorageAccount                          account   = new CloudStorageAccount(credentials, true);

        CloudBlobClient                           blobClient   = new CloudBlobClient(account.BlobEndpoint.AbsoluteUri, account.Credentials);

        CloudBlobContainer                cloudBlobcontainer   = blobClient.GetContainerReference(containerName);

        CloudBlockBlob                                  blob   = cloudBlobcontainer.GetBlockBlobReference(filename);

     

        blob.DownloadToStream(memStream);

        storageResult2 = BankStatementImportUtils::sendFileToTempStore(memStream, filename);


        ttscommit;


        return   storageResult2;


    }


    /// <summary>

    /// The constructor of the bank statement import batch.

    /// </summary>

    /// <returns>

    /// The instance of the <c>BankStatementImportBatch</c> class.

    /// </returns>

    public static HMT_BankStatementImportProcess construct()

    {

        HMT_BankStatementImportProcess import = new HMT_BankStatementImportProcess();


        return import;

    }


}


-----


/// <summary>

/// Contract class for bank statement import via blob storage

/// </summary>

[DataContract]

[SysOperationContractProcessing(classStr(HMT_BankStatementImportSysOperationUIBuilder))]

class HMT_BankStatementImportSysOperationContract

{

    private BankStatementFormatRefRecId                 bankStatementFormatRefRecId;

    private BankReconciliationMatchAutoAfterImport      matchAutoAfterImport;


    /// <summary>

    /// BankStatementFormatRefRecId

    /// </summary>

    /// <param name = "_bankStatementFormatRefRecId">BankStatementFormatRefRecId</param>

    /// <returns>BankStatementFormatRefRecId</returns>

    [DataMember,

        SysOperationLabel(literalStr("@HMTships:HMT_BankStatementFormat")),

        SysOperationHelpText(literalStr("@HMTships:HMT_BankStatementFormatHelpText")),

        SysOperationDisplayOrder('1')]

    public BankStatementFormatRefRecId parmBankStatementFormatRefRecId(BankStatementFormatRefRecId _bankStatementFormatRefRecId = bankStatementFormatRefRecId)

    {

        bankStatementFormatRefRecId = _bankStatementFormatRefRecId;


        return bankStatementFormatRefRecId;

    }


    /// <summary>

    /// BankReconciliationMatchAutoAfterImport

    /// </summary>

    /// <param name = "_matchAutoAfterImport">BankReconciliationMatchAutoAfterImport</param>

    /// <returns>BankReconciliationMatchAutoAfterImport</returns>

    [DataMember,

        SysOperationLabel(literalStr("@HMTships:HMT_ReconcileAfterImport")),

        SysOperationHelpText(literalStr("@HMTships:HMT_ReconcileAfterImportHelpText")),

        SysOperationDisplayOrder('1')]

    public BankReconciliationMatchAutoAfterImport parmMatchAutoAfterImport(BankReconciliationMatchAutoAfterImport _matchAutoAfterImport = matchAutoAfterImport)

    {

        matchAutoAfterImport = _matchAutoAfterImport;


        return matchAutoAfterImport;

    }


}


---------


/// <summary>

/// Controller class for the bank statement import batch

/// </summary>

class HMT_BankStatementImportSysOperationController extends SysOperationServiceController

{

    /// <summary>

    /// New method of the controller

    /// </summary>

    protected void new()

    {

        super(classStr(HMT_BankStatementImportSysOperationService), methodStr(HMT_BankStatementImportSysOperationService, process), SysOperationExecutionMode::Synchronous);

    }


    /// <summary>

    /// default caption batch

    /// </summary>

    /// <returns>ClassDescription</returns>

    public ClassDescription defaultCaption()

    {

        const str  bankStmtImportBatchCaption = "@HMTships:HMT_BankStatementImportBatch";

        return bankStmtImportBatchCaption;

    }


    /// <summary>

    /// Construct method

    /// </summary>

    /// <param name = "_executionMode">SysOperationExecutionMode</param>

    /// <returns>HMT_BankStatementImportSysOperationController</returns>

    public static HMT_BankStatementImportSysOperationController construct(SysOperationExecutionMode _executionMode = SysOperationExecutionMode::Synchronous)

    {

        HMT_BankStatementImportSysOperationController controller;

        controller = new HMT_BankStatementImportSysOperationController();

        controller.parmExecutionMode(_executionMode);

        return controller;

    }


    /// <summary>

    /// Main method of the controller

    /// </summary>

    /// <param name = "_args">Args</param>

    public static void main(Args _args)

    {

        HMT_BankStatementImportSysOperationController controller;

        controller = HMT_BankStatementImportSysOperationController::construct();

        controller.parmArgs(_args);

        controller.startOperation();

    }


}


--------------------------


/// <summary>

/// Service class

/// </summary>

class HMT_BankStatementImportSysOperationService extends SysOperationServiceBase

{

    /// <summary>

    /// Process method that will import the bank statement from Azure Blob

    /// </summary>

    /// <param name = "_contract">HMT_BankStatementImportSysOperationContract</param>

    public void process(HMT_BankStatementImportSysOperationContract _contract)

    {

        HMT_BankStatementImportProcess object         = new HMT_BankStatementImportProcess();

        BankParameters                 bankParameters = bankParameters::find();


        if(bankParameters.BlobAccountName != null &&

            bankParameters.BlobFailed != null &&

            bankParameters.BlobPublicKey != null &&

            bankParameters.BlobReference != null &&

            bankParameters.BlobSuccessful != null)

        {

            object.runProcess(_contract.parmBankStatementFormatRefRecId(), _contract.parmMatchAutoAfterImport());

        }

        else

        {

            Info("@HMTships:HMT_BankImportStatementMandatoryFields");

        }

    }


}


-----------------------------


/// <summary>

/// UI Builder class

/// </summary>

class HMT_BankStatementImportSysOperationUIBuilder extends SysOperationAutomaticUIBuilder

{

    HMT_BankStatementImportSysOperationContract contract;

    DialogField                             dialogBankStatementFormat, dialogReconcileAfterImport;

    /// <summary>

    /// Build method

    /// </summary>

    public void build()

    {

        DialogGroup dlgGrp;

        //Get current dialog

        Dialog dlg = this.dialog();

        dlgGrp = dlg.addGroup("");

        dlgGrp.columns(2);


        contract = this.dataContractObject();

        dialogBankStatementFormat        = this.addDialogField(methodStr(HMT_BankStatementImportSysOperationContract, parmBankStatementFormatRefRecId), contract);

        dialogReconcileAfterImport    = this.addDialogField(methodStr(HMT_BankStatementImportSysOperationContract, parmMatchAutoAfterImport), contract);


    }


    /// <summary>

    /// PostRun

    /// </summary>

    public void postRun()

    {

        //super();

    }


}


------------------------------------------


/// <summary>

/// Helper class for blob storage

/// </summary>

using Microsoft.WindowsAzure.Storage;

using Microsoft.WindowsAzure.Storage.Blob;

using Azure.Storage.Blobs;

using Azure.Storage.Blobs.Models;

class HMT_BlobFileHelper

{

    /// <summary>

    /// Get file names from blob

    /// </summary>

    /// <param name = "_azureBlobName">_azureBlobName</param>

    /// <param name = "_azureBlobAccessKey">_azureBlobAccessKey</param>

    /// <param name = "_azureContainerReference">_azureContainerReference</param>

    /// <returns>List</returns>

    public static List getFileNamesFromBlob(str _azureBlobName, str _azureBlobAccessKey, str _azureContainerReference)

    {

        

        str                             azureBlobName           = _azureBlobName;

        str                             azureBlobAccessKey      = _azureBlobAccessKey;

        str                             azureContainerReference = _azureContainerReference;

        List                            filenames               = new List(Types::String);

        var storageCredentials  = new Microsoft.WindowsAzure.Storage.Auth.StorageCredentials(azureBlobName, azureBlobAccessKey);


        CloudStorageAccount storageAccount = new Microsoft.WindowsAzure.Storage.CloudStorageAccount(storageCredentials, true);

        CloudBlobClient     blobClient     = storageAccount.CreateCloudBlobClient();

        CloudBlobContainer  blobContainer  = blobClient.GetContainerReference(azureContainerReference);

        System.Collections.IEnumerable list = new System.Collections.Generic.List<str>();

        list = blobContainer.ListBlobs(null, false, 0, null, null);

        System.Collections.IEnumerator listEnumerator = list.getEnumerator();


        while  (listEnumerator.moveNext())

        {

            CloudBlockBlob blob = listEnumerator.get_Current();


            if (blob.Exists(null, null))

            {

                str file     = blob.StorageUri.PrimaryUri.AbsoluteUri;

                str fileName = blob.Name;

                Info(strFmt("@HMTships:HMT_BlobFileName", fileName));

                filenames.addStart(fileName);

            }

        }


        return filenames;

    }


    /// <summary>

    /// Delete bank statement and transfer to blob

    /// </summary>

    /// <param name = "_azureBlobName">_azureBlobName</param>

    /// <param name = "_azureBlobAccessKey">_azureBlobAccessKey</param>

    /// <param name = "_azureContainerReference">_azureContainerReference</param>

    /// <param name = "_azureFileName">_azureFileName</param>

    /// <param name = "_azureBlobDestination">_azureBlobDestination</param>

    public static void deleteBankStatementAndTransfer(str _azureBlobName, str _azureBlobAccessKey, str _azureContainerReference, str _azureFileName, str _azureBlobDestination)

    {

        str                             azureBlobName           = _azureBlobName;

        str                             azureBlobAccessKey      = _azureBlobAccessKey;

        str                             azureContainerReference = _azureContainerReference;

        var storageCredentials = new Microsoft.WindowsAzure.Storage.Auth.StorageCredentials(azureBlobName, azureBlobAccessKey);


        CloudStorageAccount storageAccount = new Microsoft.WindowsAzure.Storage.CloudStorageAccount(storageCredentials, true);

        CloudBlobClient    blobClient      = storageAccount.CreateCloudBlobClient();

        CloudBlobContainer blobContainer   = blobClient.GetContainerReference(azureContainerReference);

        CloudBlockBlob     blob            = blobContainer.GetBlockBlobReference(_azureFileName);


        CloudBlobContainer destinationCloudBlobContainer = blobClient.GetContainerReference(_azureBlobDestination);

        CloudBlockBlob     sourceBlob                    = blob;

        CloudBlockBlob     destinationBlob               = destinationCloudBlobContainer.GetBlockBlobReference(_azureFileName);


        destinationBlob.UploadFromStream(sourceBlob.OpenRead(null, null, null), null, null, null);


        sourceBlob.Delete(0,null,null,null);


    }


}


-----------------------------------------

/// <summary>

/// Utils for exception processing.

/// </summary>

using Microsoft.Dynamics365.LocalizationFramework;

using SL = Microsoft.Dynamics365.LocalizationFramework.XppSupportLayer;


using Microsoft.Dynamics.ApplicationPlatform.Environment;

using Microsoft.Dynamics.ApplicationPlatform.XppServices.Instrumentation;


/// <remarks>

/// THIS IS AN INTERNAL CLASS, API COMPATIBILITY IS NOT GUARANTEED IN THE FUTURE.

/// </remarks>

internal final class HMT_ERExceptionUtils

{

    

    private static System.Exception latestException;

    /// <summary>

    /// Creates an infolog messages for all exceptions in the result log.

    /// </summary>

    /// <param name = "_runResult">A failed result.</param>

    /// <param name = "_errorPoint">A label describing the entry point that is currently being run, eg: format mapping or model mapping.</param>

    [Hookable(false)]

    public static void processFailedRunExceptionsToInfolog(ERIRunResult _runResult, str _errorPoint)

    {

        var evaluationFailedText = strFmt("@ElectronicReporting:UnexpectedErrorDuringRun", _errorPoint);

        if (_runResult.SolutionErrors != null && _runResult.SolutionErrors.Count > 0)

        {

            setPrefix(evaluationFailedText);

            error(evaluationFailedText);

            var errorsCount = _runResult.SolutionErrors.Count;

            var currentErrorNum = 1;

            var errorsEnumerator = _runResult.SolutionErrors.GetEnumerator();

            while(errorsEnumerator.moveNext())

            {

                ERIRuntimeError solutionError = errorsEnumerator.Current;


                if (currentErrorNum == errorsCount)

                {

                    throw error(solutionError.Message);

                }


                error(solutionError.Message);

                currentErrorNum++;

            }

        }

        else

        {

            throw error(evaluationFailedText);

        }

    }


}


----------------------------------------------

Form: BankParameters.HMTships

Added Blob storage fields to design

AzureAccountDetails_BlobAccountName
AzureAccountDetails_BlobReference
AzureAccountDetails_BlobFailed
AzureAccountDetails_BlobPublicKey
AzureAccountDetails_BlobSuccessful
------------------------------------------------------
Menu Extensions:
CashAndBankManagement.HMTships

------------------------------------------------------
BankParameters.HMTships

Add fields 

BlobAccountName

BlobReference

BlobFailed

BlobPublicKey

BlobSuccessful


No comments:

Post a Comment

Financial reporting DataMart reset in D365 FO X++

  https://community.dynamics.com/blogs/post/?postid=f866f228-4ca5-4013-b996-ec2fe9dde72e