Thursday 28 June 2018

Hierarchical report in SSRS to show users and their managers


Tuesday 26 June 2018

To enable and disable a field based on value in other field


To enable and disable a field based on value in other field :


class PurchTableFormExtension
{
    [FormDataSourceEventHandler(formDataSourceStr(PurchTable, PurchLine), FormDataSourceEventType::Activated)]
    public static void PurchLine_OnActivated(FormDataSource sender, FormDataSourceEventArgs e)
    {
        FormRun             formRun          = sender.formRun();
        FormDataSource      PurchTable_ds   = formRun.dataSource(formDataSourceStr(PurchTable, PurchTable)) as FormDataSource;
        PurchTable          purchTable= PurchTable_ds.cursor();

        FormStringControl    NCMRNo = formRun.design(0).controlName("NCMR_OINCMRNum_011");
        FormStringControl    NCMRDispositionCode = formRun.design(0).controlName("NCMR_OINCMRDispositionCodeId_011");
        if(NCMRNo.text() != "")
            NCMRDispositionCode.enabled(true);
        else
        {
            NCMRDispositionCode.text(" ");
            NCMRDispositionCode.enabled(false);
         
        }
    }

        /// <summary>
    ///
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    [FormControlEventHandler(formControlStr(PurchTable, NCMR_OINCMRNum_011), FormControlEventType::Modified)]
    public static void NCMR_OINCMRNum_011_OnModified(FormControl sender, FormControlEventArgs e)
    {
        FormRun             formRun          = sender.formRun();
        FormDataSource      PurchTable_ds   = formRun.dataSource(formDataSourceStr(PurchTable, PurchTable)) as FormDataSource;
        PurchTable          purchTable= PurchTable_ds.cursor();
        FormDataSource      PurchLine_ds   = formRun.dataSource(formDataSourceStr(PurchTable, PurchLine)) as FormDataSource;
        PurchLine           purchline= PurchLine_ds.cursor();

        FormStringControl    NCMRNo = formRun.design(0).controlName("NCMR_OINCMRNum_011");
        FormStringControl    NCMRDispositionCode = formRun.design(0).controlName("NCMR_OINCMRDispositionCodeId_011");
        if(NCMRNo.text() != "")
            NCMRDispositionCode.enabled(true);
        else
        {
            purchline.OINCMRDispositionCodeId_011   =   "";
            NCMRDispositionCode.enabled(false);
        }
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    [FormControlEventHandler(formControlStr(PurchTable, NCMR_OINCMRNum_011), FormControlEventType::LostFocus)]
    public static void NCMR_OINCMRNum_011_OnLostFocus(FormControl sender, FormControlEventArgs e)
    {
        FormRun             formRun          = sender.formRun();
        FormDataSource      PurchTable_ds   = formRun.dataSource(formDataSourceStr(PurchTable, PurchTable)) as FormDataSource;
        PurchTable          purchTable= PurchTable_ds.cursor();

        FormStringControl    NCMRNo = formRun.design(0).controlName("NCMR_OINCMRNum_011");
        FormStringControl    NCMRDispositionCode = formRun.design(0).controlName("NCMR_OINCMRDispositionCodeId_011");
        if(NCMRNo.text() != "")
            NCMRDispositionCode.enabled(true);
        else
        {
            NCMRDispositionCode.text(" ");
            NCMRDispositionCode.enabled(false);
         
        }
    }
===============================================================
//Enable and Disable field "Extra Calculationday" based on Enum value for the field "CreditCheckType".

[FormControlEventHandler(formControlStr(CustParameters, FormGroupControl1_FINCustCreditCheckType), FormControlEventType::Modified)]
    public static void FormGroupControl1_FINCustCreditCheckType_OnModified(FormControl sender, FormControlEventArgs e)
    {
        FormRun             formRun          = sender.formRun();
        FormComboBoxControl    finCreditCheckType = formRun.design(0).controlName("FormGroupControl1_FINCustCreditCheckType");
        FormIntControl    extraCalculationDays = formRun.design(0).controlName("FormGroupControl1_EQNExtraCalculationDays");
     
        if(finCreditCheckType.valueStr() == enum2str(FINCustCreditCheckType::BasedonExposure))
        {
            extraCalculationDays.enabled(true);
        }
        else
        {
            extraCalculationDays.enabled(false);
        }
    }

==============================================================

    /// <summary>
    ///
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    [FormControlEventHandler(formControlStr(PurchTable, NCMR_OINCMRDispositionCodeId_011), FormControlEventType::Lookup)]
    public static void NCMR_OINCMRDispositionCodeId_011_OnLookup(FormControl sender, FormControlEventArgs e)
    {
        FormRun             formRun          = sender.formRun();
        FormStringControl    NCMRDispositionCode = formRun.design(0).controlName("NCMR_OINCMRDispositionCodeId_011");
        Query query = new Query();
        QueryBuildDataSource qbds;
        SysTableLookup sysTableLookup;
        sysTableLookup = SysTableLookup::newParameters(tableNum(OINCMRDispositionCode), sender);
        sysTableLookup.addLookupfield(fieldNum(OINCMRDispositionCode,NCMRDispositionCodeId));
        sysTableLookup.addLookupfield(fieldNum(OINCMRDispositionCode, Description));
        qbds = query.addDataSource(tableNum(OINCMRDispositionCode));
        sysTableLookup.parmQuery(query);
        sysTableLookup.performFormLookup();
    }

}

Code to execute DMF through Code

Move Data from File to staging :

 Void fileToDMStaging()
    {
       
        DMFDefinitionGroupExecution     groupExecution;
        DMFDefinitionGroupEntity    dMFDefinitionGroupEntity;
        definitionGroup = DMFDefinitionGroup::find(CustParameters::find().CITImportProject); 
// Field of Extended Data type : "DMFDefinitionGroupName" Created in CustParameters which //should be selected . - Process group created can be shown in this field.

        executionId             = DMFUtil::generateExecutionId(CustParameters::find().CITImportProject);
        Description description = strFmt('Execution - %1 for Definition Group - %2', executionId, CustParameters::find().CITImportProject);
        DMFDefinitionGroupExecution::insertOrDisplay(definitionGroup, executionId, description, false);
        select entity from dMFDefinitionGroupEntity where dMFDefinitionGroupEntity.DefinitionGroup == definitionGroup.DefinitionGroupName;
        ttsbegin;
        groupExecution = DMFDefinitionGroupExecution::find(definitionGroup.DefinitionGroupName,dMFDefinitionGroupEntity.Entity,executionId,true);

// Below code is written , as it takes filepath set to process group instead we need to use contract //parameter file path :

        groupExecution.FilePath = /*"C4PCUATTF";*/filePath;
        groupExecution.FilePath = groupExecution.applyTransforms(groupExecution.FilePath);
       
        groupExecution.ExcelLookUp =
                        DMFDefinitionGroupEntity::insertExcelLookUpValue(
                        groupExecution.DefinitionGroup,
                        groupExecution.Entity,
                        groupExecution.FilePath,
                        groupExecution.Source);
        groupExecution.update();
        ttscommit;
        DMFStagingWriterContract        contract = new DMFStagingWriterContract();
        DMFQuickImportExport::doPGImport(definitionGroup.DefinitionGroupName ,executionId);//Need to create extension of class DMFQuickImportExport to pass File path and assign it to the contract variable
    }


Move Data from Staging to Target :

 Void stagingToTarget()
        {
            DmfDefinitionGroupExecution dmfDefinitionGroupExecution;
            dmfDefinitionGroupExecution=dmfDefinitionGroupExecution::find(CustParameters::find().CITImportProject,'Customer payment journal line',executionId);
            if (dmfDefinitionGroupExecution.ExecutionId)
            {
                ttsbegin;
                DMFDefinitionGroupExecution defGroupExec;

                update_recordset defGroupExec
                setting IsSelected = NoYes::yes,
                    ExecuteTargetStep = NoYes::Yes
                where defGroupExec.ExecutionId == DMFDefinitionGroupExecution.ExecutionId;

                ttscommit;
            }

            DMFentitywriter  Dmfentitywriter = new DMFentitywriter();
            Dmfentitywriter.parmEntityTableName('CustomerPaymentJournalLineStaging');
            Dmfentitywriter.parmDMFExecution(DMFExecution::find(executionId));
            Dmfentitywriter.parmDMFDefinitionGroupExecution(dmfDefinitionGroupExecution);
            Dmfentitywriter.parmExecutionId(executionId);
            Dmfentitywriter.run();
           
        }

Using DMF while creating Payment journal settle the Invoice with out posting

Using DMF creating Payment journal and mark the settle lines :

We need to extend CustomerPaymentJournalLineEntity - Data entity.

[ExtensionOf(tableStr(CustomerPaymentJournalLineEntity))]
Final class CustomerPaymentJournalLineEntity_CIT_Extension
{
    [PostHandlerFor(tableStr(CustomerPaymentJournalLineEntity), tableMethodStr(CustomerPaymentJournalLineEntity, mapEntityToDataSource))]
    public static void CustomerPaymentJournalLineEntity_Post_mapEntityToDataSource(XppPrePostArgs args)
    {
        CustTable   custtable;
        CustomerPaymentJournalLineEntity    customerPaymentJournalLineEntity = args.getThis() as CustomerPaymentJournalLineEntity;
        custtable   =   CustTable::find(customerPaymentJournalLineEntity.AccountDisplayValue);
        DataEntityDataSourceRuntimeContext dataSourceCtx    = args.getArg(identifierStr(_dataSourceCtx));
        switch (dataSourceCtx.name())
        {
            case dataEntityDataSourceStr(CustomerPaymentJournalLineEntity, LedgerJournalTrans):
                LedgerJournalTrans ledgerJournalTrans = dataSourceCtx.getBuffer();
                ledgerJournalTrans.initFromCustTable(custtable);
                /*if(!ledgerJournalTrans.MarkedInvoice)
                {
                    ledgerJournalTrans.CITErrorLog  =   NoYes::Yes; // if any error occurs while importing record from file a  field added to ledgerjournaltrans to notify it.
                }*/
                if ( LedgerJournalTrans.MarkedInvoice)
                {
                    if(!CustTrans::findFromInvoice(ledgerJournalTrans.MarkedInvoice).RecId)
                    {
                        ledgerJournalTrans.CITErrorLog  =   NoYes::Yes;
                    }
                }
                break;
        }

    }

    [DataEventHandler(tableStr(CustomerPaymentJournalLineEntity), DataEventType::PersistedEntity)]
    public static void CustomerPaymentJournalLineEntity_onPersistedEntity(Common _sender, DataEventArgs _eventArgs)
    {
        CustomerPaymentJournalLineEntity customerPaymentJournalLineEntity = _sender;
        LedgerJournalTrans      ledgerJournalTrans;
                     
        while select ledgerJournalTrans where customerPaymentJournalLineEntity.JournalBatchNumber == ledgerJournalTrans.JournalNum
            && customerPaymentJournalLineEntity.LineNumber == ledgerJournalTrans.LineNum
        {
            CustomerPaymentJournalLineEntity::insertLinesForLineLevelSettlement(ledgerJournalTrans,customerPaymentJournalLineEntity.CITDiscountAmount);
        }
    }

    private static void insertLinesForLineLevelSettlement(LedgerJournalTrans _ledgerJournalTrans, Amount _cashDisc)
    {
        CustTransOpen               custTransOpen;
        custTrans                   custTrans;
        CustVendOpenTransManager    manager;
        CustTransCashDisc           custTransCashDisc;

        Select custTransOpen where custTransOpen.accountNum == _ledgerJournalTrans.accountDisplay()
            Join custTrans where custTrans.Invoice == _ledgerJournalTrans.MarkedInvoice && custTrans.RecId == custTransOpen.RefrecId;
        If (custTransOpen)
        {
            if (_cashDisc)
            {
                custTransCashDisc.CashDiscAmount = _cashDisc;
                custTransCashDisc.CashDiscdate = systemdateget();
                custTransCashDisc.RefRecId = custTransOpen.RecId;
                custTransCashDisc.RefTableId = custTransOpen.TableId;
                custTransCashDisc.insert();
            }
            /*
            manager = CustVendOpenTransManager::construct(_ledgerJournalTrans);
            manager.updateSettleAmount(custTransOpen,_ledgerJournalTrans.AmountCurCredit);
            manager.updateTransMarked(custTransOpen,true);
            */
            SpecTrans specTrans;

            specTrans.SpecCompany = _ledgerJournalTrans.DataAreaId;
            specTrans.SpecRecId = _ledgerJournalTrans.RecId;
            specTrans.SpecTableId = _ledgerJournalTrans.TableId;
            specTrans.RefCompany = custTransOpen.DataAreaId;
            specTrans.RefRecId = custTransOpen.RecId;
            specTrans.RefTableId = custTransOpen.TableId;
            specTrans.code = _ledgerJournalTrans.CurrencyCode;
            specTrans.Balance01 = _ledgerJournalTrans.AmountCurCredit;
            specTrans.Payment = NoYes::No;
            specTrans.SelectedDateUsedToCalcCashDisc = today();
            specTrans.insert();

            ttsBegin;

            _ledgerJournalTrans.selectForUpdate(true);
            _ledgerJournalTrans.SettleVoucher    = SettlementType::SelectedTransact;
            _ledgerJournalTrans.update();

            ttsCommit;
        }
    }

}

=======================================================================

To Address discount while importing File:

Extend CustomerPaymentJournalLineEntity and add CITDiscountAmount.

If you want any of the fields to get populated from file values we need to add these fields if not exists
Ex: Document Number



Call other form/ Class with Button Click

Call other form/ Class with Button Click 


 [FormControlEventHandler(formControlStr(LedgerJournalTransCustPaym, CustInRemitance), FormControlEventType::Clicked)]
    public static void CustInRemitance_OnClicked(FormControl sender, FormControlEventArgs e)
    {
   
        FormDatasource          fds;
        MenuFunction            menuFunction;
        LedgerJournalTrans ljt;
        Args    args =  new Args();
       
        fds                 = sender.formRun().datasource('LedgerJournalTrans');
         ljt   = fds.cursor();
        args.parm(ljt.JournalNum);
        menuFunction = new MenuFunction(menuItemActionStr(CITCustPaymImport),
            MenuItemType::Action);
        if (menuFunction.hasRunPermissions(args))
        {
            menuFunction.run(args);
        }


    }

Filter Grid in D365 based on lookup of Some unbound control

I have  a task to filter the grid based on unbound Enum control ( Show - All/ErrorLines)

class CITLedgerJournalTransCustPaym
{
    QueryBuildRange        citErrorLog;

   
    /// <summary>
    ///
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    [FormControlEventHandler(formControlStr(LedgerJournalTransCustPaym, Show), FormControlEventType::SelectionChanged)]
    public static void Show_OnSelectionChanged(FormControl sender, FormControlEventArgs e)
    {
        FormDatasource          fds;
        FormControl             CITError;
        FormRun                 element;
        QueryBuildRange        citErrorLog;
       
        element     = sender.FormRun();
        CITError    =   element.design(0).controlName('Show');
        fds         = element.dataSource(formDataSourceStr(LedgerJournalTransCustPaym, LedgerJournalTrans)) as FormDataSource;
        citErrorLog = fds.query().dataSourceTable(tableNum(LedgerJournalTrans)).findRange(fieldNum(LedgerJournalTrans,CITErrorLog));
       
        if(citErrorLog == null)
        {
            citErrorLog  = fds.query().dataSourceTable(tableNum(LedgerJournalTrans)).addRange(fieldNum(LedgerJournalTrans, CITErrorLog));
        }
     
        if(CITError.valueStr() == enum2Str(CITShow::Errorlines))
        {
            citErrorLog.value(enum2Str(NoYes::Yes));
        }
        else
        {
            citErrorLog.value(SysQuery::valueUnlimited());
           // ;
        }
       
        fds.executeQuery();
        fds.reread();
        fds.refresh();
       
        //QueryBuildRange = Table_ds.query().dataSourceName(“datasource name”).addRange(fieldNum(Table,field)).value(enum2str(enum.selection()));
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    [FormDataSourceEventHandler(formDataSourceStr(LedgerJournalTransCustPaym, LedgerJournalTrans), FormDataSourceEventType::Initialized)]
    public static void LedgerJournalTrans_OnInitialized(FormDataSource sender, FormDataSourceEventArgs e)
    {
        //fds                 = sender.formRun().datasource('LedgerJournalTrans');
        //citErrorLog = fds.query().dataSourceTable(tableNum(LedgerJournalTrans)).addRange(fieldNum(LedgerJournalTrans,CITErrorLog));

    }


}

Disable and enable Menu items on form

Disable and enable menu item in form using record data :

class CITLedgerJournalTransCustPaym
{
    QueryBuildRange        citErrorLog;
// Below code is used to get
    [FormDataSourceEventHandler(formDataSourceStr(LedgerJournalTransCustPaym, LedgerJournalTrans), FormDataSourceEventType::Activated)]
    public static void LedgerJournalTrans_OnActivated(FormDataSource sender, FormDataSourceEventArgs e)
    {
        FormDatasource      fds;
        LedgerJournalTrans  LedgerJournalTrans;
        LedgerJournalName   ledgerJournalName;
        FormControl         CITRemitImport,CITCustImport;
        FormRun             element;
     
        fds                 = sender.formRun().datasource('LedgerJournalTrans');
        LedgerJournalTrans   = fds.cursor();
        element             = sender.FormRun();
        CITRemitImport      = element.design(0).controlName('CustInRemitance');
        CITCustImport       = element.design(0).controlName('CustInpayment');
        ledgerJournalName   = LedgerJournalName::find(ledgerJournalTrans.ledgerJournalTable().JournalName);

        if(ledgerJournalName.CITRemitImport)
        {
            CITRemitImport.visible(true);
            CITCustImport.visible(false);
        }
        else
        {
            CITRemitImport.visible(false);
            CITCustImport.visible(true);
        }
    }

 

}

Batch Job In D365

Batch Job In D365 :

Contract Class:
[
    DataContractAttribute,
    SysOperationContractProcessingAttribute(classStr(CITCustPaymImportUIBuilder))
]
    class CITCustPaymImportContract implements SysOperationValidatable
{
    CustAccount     custAccount;
    FilePath        filePath;
    FileUpload      filepath1;
    JournalId       journalNum;
    TransDate       postingDate;
    CustGroupId     custGroup;
    [
        //DataMemberAttribute(identifierStr(Filepath)),     
        DataMemberAttribute('Filepath'),
         SysOperationLabelAttribute('Upload document'),
        SysOperationDisplayOrderAttribute('3'),
        SysOperationHelpTextAttribute(literalStr("Assign file path"))
    ]
    public FilePath parmFilePath(FilePath _filePath = filePath)
    {
        filePath = _filePath;
        return filePath;
    }

    [
        //DataMemberAttribute(identifierStr(CustAccount))
        DataMemberAttribute('CustAccount'),
        SysOperationLabelAttribute('Customer Account'),
        SysOperationDisplayOrderAttribute('1'),
        SysOperationHelpTextAttribute(literalStr("Customer Account no."))
    ]
    public CustAccount parmCustAccount(CustAccount _custAccount = custAccount)
    {
        custAccount = _custAccount;
        return custAccount;
    }

    [
        //DataMemberAttribute(identifierStr(JournalNum)),
        DataMemberAttribute('JournalNum'),
        SysOperationControlVisibilityAttribute(false),
        SysOperationDisplayOrderAttribute('4'),
        SysOperationHelpTextAttribute(literalStr("JournalNum"))
    ]
    public JournalId parmJournalNum( JournalId _journalNum = journalNum)
    {
        journalNum = _journalNum;
        return journalNum;
    }

    [
        //DataMemberAttribute(identifierStr(Postingdate)),
        DataMemberAttribute('Postingdate'),
        SysOperationLabelAttribute('Posting Date'),
        SysOperationDisplayOrderAttribute('2'),
        SysOperationHelpTextAttribute(literalStr("Posting Date"))
    ]
    public TransDate parmPostingDate(TransDate _postingDate = postingDate)
    {
        postingDate = _postingDate;
        return postingDate;
    }

    public boolean validate()
    {
        boolean isValid = true;
        if (!custAccount)
        {
            isValid = checkFailed(strFmt("@SYS84753", custAccount));
        }
        /*if (!filePath)
        {
            isValid = checkFailed(strFmt("@SYS84753", filePath));
        }*/
       
        return isValid;
    }

}
=======================================================================
Controller : 

class CITCustPaymImportController extends SysOperationServiceController
{
    Common  callerRecord;
   
 
    void new()
    {
        super();

        this.parmClassName(classStr(CITCustPaymImportService));
        this.parmMethodName(methodStr(CITCustPaymImportService, process));
        this.parmDialogCaption('Customer payments Import');
    }

    public static void main(Args _args)
    {
        CITCustPaymImportController controller;
        controller = new CITCustPaymImportController();
     
        controller.parmShowDialog(true);
        CITCustPaymImportContract contract;
        contract    =   controller.getDataContractObject();
        //contract    =   this.parmReportContract().parmRdpContract() as CITCustPaymImportContract;
        if(_args.parm())
        {
            //ledgerJournalTrans = this.parmCallerRecord() as LedgerJournalTrans;
            contract.parmJournalNum(_args.parm());
        }
        //controller.initFromCaller(); 
        controller.startOperation(); 
        //controller.refreshCallerRecord();
    }

    public LabelType parmDialogCaption(LabelType _dialogCaption = "")
    {
        LabelType caption;
        caption = "Customer payments Import";
        return caption;
    }

    public boolean canGoBatch()
     {
        return false;
     }

    public Common parmCallerRecord(Common _callerRecord = callerRecord)
    {
        callerRecord = _callerRecord;
        return callerRecord;
    }

    ///    Refreshes the calling form data source.
    protected void refreshCallerRecord()
    {
        FormDataSource callerDataSource;
        if (this.parmCallerRecord() && this.parmCallerRecord().dataSource())
        {
            callerDataSource = this.parmCallerRecord().dataSource();
            callerDataSource.research(true);
        }
     }

}
======================================================================
Service :

class CITCustPaymImportService extends SysOperationServiceBase
{
    DMFExecutionId      executionId;
    DMFDefinitionGroup  definitionGroup;
    CustAccount     custAccount;
    FilePath        filePath,filepath1;
    JournalId       journalNum;
    TransDate       postingDate;
    Map                 uniqueKeyFieldListStagingMap,stagingTargetFieldListMap, autoGenStagingMap;
    Map                 entityPermissionMap;
    guid                scopeIdentifier;
    boolean             processComposite;
    Batch           currentBatch;
FileUpload             fileUpload;
map                           mapInv;
boolean             isMapEnd;
    public void process(CITCustPaymImportContract _contract)
    {
        filePath        = _contract.parmFilePath();
        custAccount     = _contract.parmCustAccount();
        journalNum      = _contract.parmJournalNum();
        postingDate     = _contract.parmPostingDate();
        try
        {
            This.processOperations(custAccount,filePath,journalNum,postingDate);
        }
        catch (Exception::Deadlock)
        {
            retry;
        }

        catch (Exception::UpdateConflict)
        {
            if (appl.ttsLevel() == 0)
            {
                /*   if (xSession::currentRetryCount() >= #RetryNum)
                {
                throw Exception::UpdateConflictNotRecovered;
                }
                else
                {
                retry;
                }*/
            }
                else
                {
                    throw Exception::UpdateConflict;
                }
        }

        catch (Exception::Error)
        {
            error(strFmt("Error occured"));
            retry;
        }
    }

    Void processOperations(CustAccount _custAccount,FilePath  _filePath,JournalId _journalNum,TransDate _postingDate)
    {
 
        This. fileToDMStaging ();
        This. ProcessStagingData(_custAccount,_journalNum,_postingDate);
        This. stagingToTarget();
    }

    //This method execution will import the data from file to staging table
    Void fileToDMStaging()
    {
       
        DMFDefinitionGroupExecution     groupExecution;
        DMFDefinitionGroupEntity    dMFDefinitionGroupEntity;
        definitionGroup = DMFDefinitionGroup::find(CustParameters::find().CITImportProject);
        executionId             = DMFUtil::generateExecutionId(CustParameters::find().CITImportProject);
        Description description = strFmt('Execution - %1 for Definition Group - %2', executionId, CustParameters::find().CITImportProject);
        DMFDefinitionGroupExecution::insertOrDisplay(definitionGroup, executionId, description, false);
        select entity from dMFDefinitionGroupEntity where dMFDefinitionGroupEntity.DefinitionGroup == definitionGroup.DefinitionGroupName;
        ttsbegin;
        groupExecution = DMFDefinitionGroupExecution::find(definitionGroup.DefinitionGroupName,dMFDefinitionGroupEntity.Entity,executionId,true);
        groupExecution.FilePath = /*"C4PCUATTF";*/filePath;
        groupExecution.FilePath = groupExecution.applyTransforms(groupExecution.FilePath);
       
        groupExecution.ExcelLookUp =
                        DMFDefinitionGroupEntity::insertExcelLookUpValue(
                        groupExecution.DefinitionGroup,
                        groupExecution.Entity,
                        groupExecution.FilePath,
                        groupExecution.Source);
        groupExecution.update();
        ttscommit;
        DMFStagingWriterContract        contract = new DMFStagingWriterContract();
        DMFQuickImportExport::doPGImport(definitionGroup.DefinitionGroupName ,executionId);//Need to create extension of class DMFQuickImportExport to pass File path and assign it to the contract variable
    }

    public NoYes parmError(NoYes  _errorStatus = NoYes::NO)
    {
        //  errorStatus = _errorStatus;

        return  NoYes::NO;
    }

    //This method execution will process the staging data for particular execution Id
    Void processStagingdata(CustAccount _custAccount,JournalId _journalId,TransDate _postingDate)
    {
        customerPaymentJournalLineStaging       customerPaymentJournalLineStaging;
        LedgerJournalTable                      ledgerjournaltable;
        JournalTableData                        journalTableData;
        ledgerjournaltable  =   LedgerJournalTable::find(journalNum,false);
        Voucher voucher;
     
        if(ledgerjournaltable)
        {
               
            voucher     =      JournalTableData::newTable(ledgerjournaltable).journalVoucherNum().getNew(true);
           
        }
       
        Update_recordSet customerPaymentJournalLineStaging
                setting DefaultDimensionsForAccountDisplayValue= _custAccount,
                AccountDisplayValue = _custAccount,
                JournalBatchNumber = _journalId,
                TransactionDate = _postingDate,
                Voucher = voucher,
                CurrencyCode = 'USD',
                DocumentNumber = customerPaymentJournalLineStaging.MarkedInvoice
                where customerPaymentJournalLineStaging.executionId == executionId;
        select customerPaymentJournalLineStaging
                where customerPaymentJournalLineStaging.executionId == executionId;

        if (!this.isInvoiceExists(customerPaymentJournalLineStaging.MarkedInvoice))
        {
            This.assignInvoiceFromPO();
        }

        /* Else
        {
        // This.sumInvoiceId();
        }*/
    }

        //Assign InvoiceId based on PO number provided in file
        Boolean isInvoiceExists(InvoiceId   _invoiceid)
        {
            boolean invexists;
            CustInvoiceJour custInvoiceJour;
            select RecId from custInvoiceJour
                where custInvoiceJour.InvoiceId ==  _invoiceid;
            if(custInvoiceJour)
                invexists   =   true;
            else
                invexists   =   false;
            return invexists;
        }

        Void assignInvoiceFromPO()
        {
            CustomerPaymentJournalLineStaging   customerPaymentJournalLineStaging;
            SalesTable                          SalesTable;
            Amount                              invoiceAmount;
            CustTransOpen                       CustTransOpen;
            CustInvoiceJour                     custInvoiceJour;
            CustTrans                           custTrans;
            CustomerPaymentJournalLineStaging   customerPaymentJournalLineStagingLoc,customerPaymentJournalLineStagingins;
           
            MapEnumerator                   mapEnum;

            While select sum(CreditAmount) from customerPaymentJournalLineStaging 
                    group by markedInvoice
                    where customerPaymentJournalLineStaging.executionId == executionId
               
            {
                select SalesTable
                    where salesTable.PurchOrderFormNum==customerPaymentJournalLineStaging.MarkedInvoice;
            mapInv = new Map(Types::String, types::Real);
                While select custInvoiceJour where custInvoiceJour.SalesId == SalesTable.SalesId
        Join custTrans where custTrans.Invoice == custInvoiceJour.InvoiceId
                    && custTrans.AccountNum == custInvoiceJour.InvoiceAccount
                    && custTrans.TransDate == custInvoiceJour.InvoiceDate
                    && custTrans.Voucher == custInvoiceJour.LedgerVoucher
                join CustTransOpen where CustTransOpen.AccountNum == custTrans.AccountNum
                                    && CustTransOpen.RefRecId == custTrans.RecId
            {
                mapInv.insert(custInvoiceJour.InvoiceId,custTransOpen.amountMST);
            }
            mapEnum = new MapEnumerator(mapInv);
            isMapEnd = false;
            if(mapEnum.moveNext())
            while select forupdate customerPaymentJournalLineStagingLoc
                where customerPaymentJournalLineStagingloc.MarkedInvoice == customerPaymentJournalLineStaging.MarkedInvoice
                                && customerPaymentJournalLineStagingloc.executionId == executionId
                {
                    mapEnum = this.assignInvoice(customerPaymentJournalLineStagingloc,mapEnum);
                    if (isMapEnd)             
                        break;
                }
            }
        }

        MapEnumerator assignInvoice(customerPaymentJournalLineStaging customerPaymentJournalLineStagingLoc,MapEnumerator mapenum)
    {
        Amount invoiceAmount;
        customerPaymentJournalLineStaging   customerPaymentJournalLineStagingins;
        invoiceAmount = mapEnum.CurrentValue();
        if (invoiceAmount >= customerPaymentJournalLineStagingLoc.CreditAmount)
        {
            ttsbegin;
            customerPaymentJournalLineStagingLoc.MarkedInvoice = mapEnum.CurrentKey();
            customerPaymentJournalLineStagingLoc.update();
            ttscommit;
            /*if (!mapenum.moveNext())
            {
                isMapEnd = true;
                return mapEnum;
            }*/
           
            if(customerPaymentJournalLineStagingloc.CreditAmount == invoiceAmount)
            {
                if (!mapenum.moveNext())
                {
                    isMapEnd = true;
                    return mapEnum;
                }
            }
           
            else
            {
                mapinv.insert(mapEnum.currentKey(),invoiceAmount-customerPaymentJournalLineStagingloc.CreditAmount);
            }
        }
        else
        {
            invoiceAmount = customerPaymentJournalLineStagingLoc.CreditAmount-invoiceAmount;
            ttsbegin;
            customerPaymentJournalLineStagingLoc.selectForUpdate(true);
            customerPaymentJournalLineStagingLoc.MarkedInvoice = mapEnum.Currentkey();
            customerPaymentJournalLineStagingLoc.CreditAmount = mapEnum.CurrentValue();
            customerPaymentJournalLineStagingLoc.update();
            ttscommit;
            customerPaymentJournalLineStagingins.data(customerPaymentJournalLineStagingloc);
            customerPaymentJournalLineStagingins.LineNumber += 0.01;
            customerPaymentJournalLineStagingins.CreditAmount =   invoiceAmount;
            customerPaymentJournalLineStagingins.RecId  =   0;
            customerPaymentJournalLineStagingins.CITDiscountAmount = 0;
            customerPaymentJournalLineStagingins.MarkedInvoice = '';
            customerPaymentJournalLineStagingins.insert();
            if (!mapenum.moveNext())
            {
                isMapEnd = true;
                return mapenum;
            }
            mapenum = this.assignInvoice(customerPaymentJournalLineStagingins,MapEnum);
        }
        return mapenum;
    }

        Void stagingToTarget()
        {
            DmfDefinitionGroupExecution dmfDefinitionGroupExecution;
            dmfDefinitionGroupExecution=dmfDefinitionGroupExecution::find(CustParameters::find().CITImportProject,'Customer payment journal line',executionId);
            if (dmfDefinitionGroupExecution.ExecutionId)
            {
                ttsbegin;
                DMFDefinitionGroupExecution defGroupExec;

                update_recordset defGroupExec
                setting IsSelected = NoYes::yes,
                    ExecuteTargetStep = NoYes::Yes
                where defGroupExec.ExecutionId == DMFDefinitionGroupExecution.ExecutionId;

                ttscommit;
            }

            DMFentitywriter  Dmfentitywriter = new DMFentitywriter();
            Dmfentitywriter.parmEntityTableName('CustomerPaymentJournalLineStaging');
            Dmfentitywriter.parmDMFExecution(DMFExecution::find(executionId));
            Dmfentitywriter.parmDMFDefinitionGroupExecution(dmfDefinitionGroupExecution);
            Dmfentitywriter.parmExecutionId(executionId);
            Dmfentitywriter.run();
           
        }

}
=======================================================================

UI Builder : 

class CITCustPaymImportUIBuilder extends SysOperationAutomaticUIBuilder
{
    Dialog      dlg;
    DialogField     dialogCustAcc;
    DialogField     dialogFilePath;
    DialogField     dialogPostingDate;
    DialogField     dialogJournalNum;
    str FileUploadName = 'FileUpload';
    private const str OkButtonName = 'Upload';
    str fileUrl;
    FileUploadBuild  dialogFileUpload;
    //dialogField     dialogFileUpload;


    CITCustPaymImportContract   contract;
    public void build()
    {
        DialogGroup dlgGrp;
        contract = this.dataContractObject();
       

        //get the current dialog
        dlg = this.dialog();

        dialogCustAcc = this.addDialogField(methodStr(CITCustPaymImportContract, parmCustAccount),contract);
        dialogCustAcc.value('');
        //dialogFilePath = this.addDialogField(methodStr(CITCustPaymImportContract, parmFilePath),contract);
        dialogPostingDate = this.addDialogField(methodStr(CITCustPaymImportContract, parmPostingDate),contract);
        dialogPostingDate.value(systemDateGet());
        dialogJournalNum = this.addDialogField(methodStr(CITCustPaymImportContract, parmJournalNum),contract);
        //make required modifications to the dialog
        dlgGrp = dlg.addGroup('FileUpload');
        dlgGrp.columns(2);
       
        FormBuildControl formBuildControl = dlg.formBuildDesign().control(dlgGrp.name());
       
        /*FileUploadBuild*/  dialogFileUpload = formBuildControl.addControlEx(classstr(FileUpload), FileUploadName);
        dialogFileUpload.baseFileUploadStrategyClassName(classstr(FileUploadTemporaryStorageStrategy));
        dialogFileUpload.fileNameLabel("@SYS308842");
        dialogFileUpload.resetUserSetting();
        dialogFileUpload.style(FileUploadStyle::MinimalWithFilename);
        dialogFileUpload.fileTypesAccepted('.xlsx');
    }

    public void dialogPostRun(DialogRunbase _dialog)
    {
        //super(_dialog);
        FileUpload fileUpload = this.getFormControl(_dialog, FileUploadName);
       // fileUpload.notifyUploadCompleted += eventhandler(dlg.uploadCompleted);
        //this.setDialogOkButtonEnabled(_dialog, false);
        FileUploadTemporaryStorageResult fileUploadResult = fileUpload.getFileUploadResult() as FileUploadTemporaryStorageResult;
        info(strfmt("-%1",fileUploadResult));
        if (fileUploadResult != null && fileUploadResult.getUploadStatus())
        {
            fileUrl = fileUploadResult.getDownloadUrl();
            info(strfmt("%1", fileUrl));
            contract.parmFilePath(fileUrl);
           
           
        }
    }

    protected FormControl getFormControl(DialogRunbase _dialog, str _controlName)
    {
        return _dialog.formRun().control(_dialog.formRun().controlId( _controlName));
    }

    private void setDialogOkButtonEnabled(DialogRunbase _dialog, boolean _isEnabled)
    {
        FormControl okButtonControl = this.getFormControl(_dialog, OkButtonName);

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

    public void run()
    {
       
        try
        {
            ttsbegin;
            FileUpload fileUploadControl = this.getFormControl(dlg, FileUploadName);
            FileUploadTemporaryStorageResult fileUploadResult = fileUploadControl.getFileUploadResult() as FileUploadTemporaryStorageResult;
            info(strfmt("-%1",fileUploadResult));
            if (fileUploadResult != null && fileUploadResult.getUploadStatus())
            {
                fileUrl = fileUploadResult.getDownloadUrl();
                info(strfmt("%1", fileUrl));
           
           
            }
            ttscommit;
        }
        catch (Exception::Deadlock)
        {
            retry;
        }
    }

    public void postBuild()
    {
        contract            =   this.dataContractObject();
        dialogCustAcc = this.bindInfo().getDialogField(
        this.dataContractObject(), methodStr(CITCustPaymImportContract, parmCustAccount));
        dialogFilePath = this.bindInfo().getDialogField(
        this.dataContractObject(), methodStr(CITCustPaymImportContract, parmFilePath));
        dialogPostingDate = this.bindInfo().getDialogField(
        this.dataContractObject(), methodStr(CITCustPaymImportContract, parmPostingDate));
        dialogJournalNum = this.bindInfo().getDialogField(
        this.dataContractObject(), methodStr(CITCustPaymImportContract, parmJournalNum));
       
    }

    private void custAccLookup(FormStringControl custAcclookup)
    {
        Query query = new Query();
        QueryBuildDataSource qbds_CustTable;
        QueryBuildRange         qbrBlocked;
        SysTableLookup sysTableLookup;

        // Create an instance of SysTableLookup with the current calling form control.
        sysTableLookup = SysTableLookup::newParameters(tableNum(CustTable), custAcclookup);
        // Add fields to be shown in the lookup form.
        sysTableLookup.addLookupfield(fieldNum(CustTable,AccountNum));
        //sysTableLookup.addLookupfield(fieldNum(CustTable,Name));
        qbds_CustTable = query.addDataSource(tableNum(CustTable));
        //this.query().dataSourceTable(tablenum(PurchTable)).addRange(fieldnum(PurchTable, PurchStatus)).value(strfmt('!%1,!%2',enum2str(PurchStatus::Canceled),enum2str(purchstatus::Invoiced)));
        //qbds_CustTable.addRange(fieldNum(CustTable, Blocked)).value(enum2str(CustVendorBlocked::All),enum2str(CustVendorBlocked::Payment));
        qbds_CustTable.addRange(fieldNum(CustTable, Blocked)).value(strfmt('!%1,!%2',enum2str(CustVendorBlocked::All),enum2str(CustVendorBlocked::Payment)));
        sysTableLookup.parmQuery(query);
        // Perform the lookup
        sysTableLookup.performFormLookup();
    }

    public void postRun()
    {
        super();

        //Register overrides for form control events
        dialogCustAcc.registerOverrideMethod(
        methodstr(FormStringControl, lookup),
        methodstr(CITCustPaymImportUIBuilder, custAccLookup),
        this);
    }

    /// <summary>
    ///
    /// </summary>
    public void getFromDialog()
    {
       
        FileUpload fileUploadControl = dlg.formRun().control(dlg.formRun().controlId(FileUploadName));
         
        FileUploadTemporaryStorageResult fileUploadResult = fileUploadControl.getFileUploadResult() as FileUploadTemporaryStorageResult;
        if (fileUploadResult != null && fileUploadResult.getUploadStatus())
        {
            fileUrl = fileUploadResult.getFileId();
        }
        contract.parmFilePath(fileUrl);
        super();
    }


}
======================================================================

Thursday 21 June 2018

Remove Cache in AX7

Remove all files in below folders :

In my case Administrator is my user.

C:\Users\Administrator\AppData\Local\Microsoft\VisualStudio\14.0\ComponentModelCache
C:\Users\Administrator\AppData\Local\Microsoft\Team Foundation\6.0\Cache
C:\Users\Administrator\AppData\Local\Temp

Thursday 7 June 2018

DMF do through code

http://axhelper.com/?p=4217
 
Purpose: The purpose of this document is to illustrate how can leverage Microsoft Dynamics AX 2012 Data Import Export Framework Web Services to automate data import and export scenarios.
 
Challenge: Microsoft Dynamics AX 2012 Data Import Export Framework is a recommended solution for data import and various integration scenarios. Data import using Data Import Export Framework consists of 2 steps: copy data from source to staging table, copy data from staging table to target. These 2 steps can be executed as 1 step. Data Import Export Framework jobs can be set up for postponed execution in batch if needed. Some of integration scenarios require a high level of automation especially when you need to invoke Data Import Export Framework jobs from external application.           
 
Solution: For data import and integration scenarios requiring a high level of automation you can leverage Data Import Export Framework Web Services. Microsoft Dynamics AX 2012 Data Import Export Framework ships with a number of Web Services which can be used for inquiries and invoking jobs. 
 
Walkthrough
 
In this walkthrough I’m going to invoke Data Import Export Framework Web Services to automatically execute “Copy from source to staging” and “Copy from staging to target” jobs from external application. For the sake of simplicity I’ll create C#.NET Console application to do so.
 
Microsoft Dynamics AX 2012 Data Import Export Framework ships with the following Web Services:
<![if !supportLists]>-        <![endif]>DMFDefinitionGroupService (create)
<![if !supportLists]>-        <![endif]>DMFEntityWriterService (targetService)
<![if !supportLists]>-        <![endif]>DMFExeStatusService (find, read)
<![if !supportLists]>-        <![endif]>DMFProcessGrpService (find, read)
<![if !supportLists]>-        <![endif]>DMFStagingService (checkConnection, exportToFile, write)
<![if !supportLists]>-        <![endif]>DMFStagingWriterService (stagingService)
 
DMFServices
 
 
Let’s take a closer look at DMF Web Services
 
DMFDefinitionGroupService
 
DMFDefinitionGroupService (Class): Creates the new mapping as per current sample file.
 
Create (Operation): This method is used to create a record in a defination group and generates a mapping between source and staging. Source may be a file,odbc or xml. By using this service from outside of AX, a user can create a single processing group with multiple entities or multi-processing group with multiple entities.
 
 
 
 
DMFEntityWriterService
 
DMFEntityWriterService (Class): The DmfEntityWriterService class is used to import data from external file to AX.
 
targetService (Operation): Service entry point to import data from a file to ax.This method will check for the processing group, if exists then updates the record set in DMFDefinitionGroupExecution table. For each entity, mapping will be created between source to staging and staging to target. Once the mapping is created successfully, it will copy the data first from source to staging, validates the data and then copies from staging to target.
 
 
DMFExeStatusService
 
DMFExeStatusService (Class): The DMFExeStatusService class serves as the document service for the DMFExeStatus document type.
 
find (Operation): Finds data objects.
 
read (Operation): Reads a DMFExeStatus document that contains one or more data objects.
 
 
DMFProcessGrpService
 
DMFProcessGrpService (Class): The DMFProcessGrpService class serves as the document service for the DMFProcessGrp document type.
 
find (Operation): Finds data objects.
 
read (Operation): Reads a DMFProcessGrp document that contains one or more data objects.
 
 
DMFStagingService
 
DMFStagingWriterService (Class): Creates the Staging Writer Service class.
 
checkConnection (Operation): Checks the writer connection.
 
exportToFile (Operation): Service entry point to export data from staging to file.
 
write (Operation): Writes in Staging.
 
Please note that write method calls executeService method listed below.
 
executeService (Operation): To import data from a file to staging.This method will check for the processing group, if exists then updates the record set in DMFDefinitionGroupExecution table. For each entity, mapping will be created between source to staging. Once the mapping is created successfully, it will copy the data from source to staging.
 
 
 
 
DMFStagingWriterService
 
DMFStagingWriter (Class): Creates the Staging Writer class.
stagingService (Operation): Runs the staging service after populating the parameters.
 
 
As I mentioned before data import using Data Import Export Framework consists of 2 steps: copy data from source to staging table, copy data from staging table to target. Please find a detailed DMF User Guide here: http://technet.microsoft.com/en-us/library/jj225591.aspx
 
 
For my scenario I’ll leverage DMFStagingWriterService and DMFEntityWriterService DMF Web Services to copy data from source to staging and copy data from staging to target correspondingly.
 
First off I’ll deploy Inbound port which exposes DMFStagingService and DMFEntityWriterService DMF Web Services operations
 
Inbound port
 
 
For simplicity I’ll import Vendor groups data using a file
 
Given
 
 
I’ll start setting up Data Import Export Framework by creating a processing group
 
Processing group
 
 
And then adding Vendor groups standard template to it
 
Select entities for processing group
 
 
Next step will be to create a simple C#.NET Console application and add a reference to DMF Web Services to it
 
C#.NET Client application: >Staging
 
 
When Service reference is added I can invoke write operation of DMFStagingService DMF Web Service in C# as depicted below
 
Source code
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DMF.DMFServiceReference;
 
namespace DMF
{
    class Program
    {
        static void Main(string[] args)
        {
            DMFStagingServiceClient client = new DMFStagingServiceClient();
           
            CallContext context = new CallContext();
            context.Company = "usmf";
 
            DMFStagingWriterContract contract = new DMFStagingWriterContract();
 
            contract.parmDefinationGroupName = "Alex";
            contract.parmExecutionID = "Alex-1";
            contract.parmEntityNameList = "Vendor groups";
            contract.parmFilePath = "C:\Users\Administrator\Desktop\Vendor groups.txt";
           
            try
            {
                client.write(context, contract);
            }
            catch
            {

            }
        }
    }
}
 
Once copy data from source to staging is done we can review Execution history
 
Execution history
 
 
As the result the data will now be in appropriate Staging table
 
Result
 
 
At this point we can also write a code to import data from staging to target using C#.NET
 
C#.NET Client application: > Target
 
 
This time I’ll need to invoke targetService operation of DMFEntityWriterService DMF Web Service in C# as depicted below
 
Source code
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DMF.DMFServiceReference;
 
namespace DMF
{
    class Program
    {
        static void Main(string[] args)
        {
            DMFEntityWriterServiceClient client = new DMFEntityWriterServiceClient();
           
            CallContext context = new CallContext();
            context.Company = "usmf";
 
            DMFEntityWriterContract contract = new DMFEntityWriterContract();
 
            contract.parmDefinationGroupName = "Alex";
            contract.parmExecutionID = "Alex-1";           
 
            try
            {
                client.targetService(context, contract, "Vendor groups""1");
            }
            catch
            {

            }
        }
    }
}
 
We can now review the result of the execution in Execution history
 
Execution history
 
 
Please note that when moving the data from staging to target the system will create a batch job. This can come handy in case you execute operations asynchronously (for example, targetServiceAsync operation) and some time is required to complete copy data from source to staging job and before copy data from staging to target job kicks off.  
 
Batch job
 
 
When batch job is done you can also review infolog details as depicted below
 
Infolog
 
 
As the result a new Vendor group record gets successfully created
 
Result
 
 
For full scenario automation 2 code snippets provided above for copy data from source to staging and copy data from staging to target can be unified into a single method which call DMF Web Services operations synchronously or asynchronously
 
Similar to how I invoked DMF Web Services operations in C#.NET I could also do this from within X++
 
Please see examples below  
 
X++ Client: > Staging
 
static void DMFStagingJob(Args _args)
{
    DMFStagingWriterService service = new DMFStagingWriterService();
    DMFStagingWriterContract contract = new DMFStagingWriterContract();
   
    contract.parmDefinationGroupName("Alex");
    contract.parmExecutionID("Alex-1");
    contract.parmEntityNameList("Vendor groups");
    contract.parmFilePath("C:\Users\Administrator\Desktop\Vendor groups.txt");
   
    try
    {
        ttsBegin;
                      
        service.write(contract);
       
        ttsCommit;
       
        info("Done!");
    }
    catch
    {
       
    }   
}
 
Result
 
 
X++ Client: >Target
 
static void DMFTargetJob(Args _args)
{
    DMFEntityWriterService service = new DMFEntityWriterService();
    DMFEntityWriterContract contract = new DMFEntityWriterContract();
   
    contract.parmDefinationGroupName("Alex");
    contract.parmExecutionID("Alex-1");
 
    try
    {
        ttsBegin;
                      
        service.targetService(contract, "Vendor groups""1");
       
        ttsCommit;
       
        info("Done!");
    }
    catch
    {
       
    }  
}
 
Result
 
 
Please note that there’re other examples of modern integrations with Microsoft Dynamics AX 2012 using DMF Web Services. For example, for MDM scenarios Microsoft Dynamics AX 2012 R3 is integrated with MDS using DMF Web Services, for data import scenarios you can also leverage the power of Data Import Export Framework in conjunction with RapidStart Services. Please find more details about MDS integration with Microsoft Dynamics AX 2012 R3 here: http://blogs.msdn.com/b/axsa/archive/2014/05/21/mdm-adapter.aspx
 

Summary: This document describes how you can leverage Microsoft Dynamics AX 2012 Data Import Export Framework Web Services to automate data import and export scenarios. This can come particularly handy in case you need to automatically execute DMF jobs from external application. We discussed what DMF Web Services are included with Microsoft Dynamics AX 2012 and how to invoke them in C#.NET and from within X++.