Wednesday, June 26, 2024

How to Use CrossCompany in a Query in Dynamics 365 F&O (X++)

While working in Dynamics 365 Finance & Operations, there are many situations where we need to read data from multiple legal entities instead of just the current company.

By default, any query or select statement in X++ only fetches records from the company you are logged into. But what if you want data from all companies?

That’s where CrossCompany comes into the picture. 

What Is CrossCompany in D365 F&O?

CrossCompany allows you to read records from multiple legal entities in a single query without changing the company context.

Normally, D365 F&O automatically applies a DataAreaId filter behind the scenes. When CrossCompany is enabled, this filter is ignored and data is returned from all companies that the user has access to.


Example: Using CrossCompany in a Query

Below is a simple example where we want to read InventLocation records from all companies.



Query query = new Query();

QueryBuildDataSource queryBuildDataSource;

QueryBuildRange queryBuildRange;

queryBuildDataSource = query.addDataSource(tableNum(InventLocation));

query.allowCrossCompany(true);

How to cancel sales line in D365 F&O using X++

If there's a requirement to cancel a sales line through code X++ in D365 F&O, the following code can be used to achieve it. In this example, I’ve created an extension of the SalesLine table and added a new method that can be called to cancel the selected sales line.

[ExtensionOf(tableStr(SalesLine))]

final class SalesLine_Extension

{

    public void gauCancelSalesOrderLine()

    {

        ttsbegin;

        this.selectForUpdate(true);

        SalesUpdateRemain::construct().updateDeliverRemainder(this, 0, 0);

        ttscommit;

    }

}

How to call Runbase batch job using X++ in D365 F&O

If there's a requirement to call a RunBaseBatch class using X++ code, the following example demonstrates how to do it. In this case, we’re using the GUPItemBasePriceCalcJob class, which is a RunBaseBatch class, and calling it by setting the appropriate parameters.        

        BatchHeader         header;

        SysRecurrenceData   sysRecurrenceData;

        Batch               batch;

        BatchJob            batchJob;

        BatchInfo           processBatchInfo;

        BatchRetries        noOfRetriesOnFailure = 4;

        GUPItemBasePriceCalcJob GUPItemBasePriceCalcJob; // RunBase batch class

        #define.timeInSecondsDelay(20)

      

        select forupdate batch

        join batchJob

        where batchJob.RecId == batch.BatchJobId

                && batch.ClassNumber == classnum(GUPItemBasePriceCalcJob)

                && batchJob.Status == BatchStatus::Waiting

                && batch.Company == curext();

        if (!batch)

        {

            // Setup the RunBaseBatch Job

            header = BatchHeader::construct();

            GUPItemBasePriceCalcJob= GUPItemBasePriceCalcJob::construct();

            GUPItemBasePriceCalcJob.parmItemId(ItemId); // pass Item Id 

            GUPItemBasePriceCalcJob.parmSiteId(SiteId); // pass Site 

            processBatchInfo = GUPItemBasePriceCalcJob.batchInfo();

            processBatchInfo.parmRetriesOnFailure(noOfRetriesOnFailure);

            header.addTask(GUPItemBasePriceCalcJob);            


            // Set the recurrence data

            sysRecurrenceData = SysRecurrence::defaultRecurrence();

            sysRecurrenceData = SysRecurrence::setRecurrenceStartDateTime(sysRecurrenceData,                             DateTimeUtil::addSeconds(DateTimeUtil::utcNow(), #timeInSecondsDelay));

            sysRecurrenceData = SysRecurrence::setRecurrenceNoEnd(sysRecurrenceData);

            sysRecurrenceData = SysRecurrence::setRecurrenceEndAfter(sysRecurrenceData, 1); 


            header.parmRecurrenceData(sysRecurrenceData);

            // Set the batch alert configurations

            header.parmAlerts(NoYes::No, NoYes::Yes, NoYes::No, NoYes::Yes, NoYes::No);

            header.save();

Tuesday, May 14, 2024

How to call SysOperation batch job using X++ in D365 F&O

ChangeItemController     controller  = ChangeItemController ::construct();

ChangeItemContract       contract    = controller ? controller.getDataContractObject() : null;


if (!contract)

        throw error(Error::wrongUseOfFunction(funcName()));


// Set contract fields

contract.parmItemId(this.ItemId);

contract.parmInventSiteId(this.SiteId );


controller.parmExecutionMode(SysOperationExecutionMode::ScheduledBatch); // Set execution mode

controller.parmShowDialog(false); 

controller.parmLoadFromSysLastValue(false); 

controller.parmInBatch(true);

controller.parmDialogCaption('Change Item');

controller.startOperation();

Friday, April 26, 2024

Create ledger journals in D365FO using X++

While working on Dynamics 365 Finance & Operations, there are many situations where we need to create ledger journals through X++ code instead of entering them manually from the UI.

This usually comes up when data is coming from an external system, a staging table, or when we want to automate accounting entries.

In this post, I’m sharing a simple approach I used to create ledger journals and journal lines programmatically using X++.

High-Level Approach

The process is straightforward:

  1. Create the journal header

  2. Initialize LedgerJournalEngine

  3. Create journal lines

  4. Set account, dimensions, debit/credit

  5. Insert the records

Step 1: Create Journal Header

// Header creation 

ledgerJournalTable.JournalNum = NumberSeq::newGetNum(LedgerParameters::numRefJournalNum()).num();

ledgerJournalTable.initFromLedgerJournalName(ledgerJournalTrans_Buffer.JournalName);

ledgerJournalTable.insert();

ledgerJournalEngine = LedgerJournalEngine::construct(ledgerJournalTable.JournalType);

ledgerJournalEngine.ledgerJournalTable(ledgerJournalTable);

ledgerJournalEngine.newJournalActive(ledgerJournalTable);


Step 2: Prepare for Journal Line Creation

//Line creation

ledgerJournalEngine.preCreate(ledgerJournalTrans);

ledgerJournalTrans.clear();

ledgerJournalTrans.initValue();

ledgerJournalTrans.JournalNum   = ledgerJournalTable.JournalNum;

ledgerJournalTrans.TransDate    = stagingTable.TransactionDate;

ledgerJournalTrans.modifiedField(fieldNum(LedgerJournalTrans, TransDate));

ledgerJournalTrans.Voucher          =   stagingTable.Voucher;

ledgerJournalTrans.CurrencyCode = stagingTable.CurrencyCode;

ledgerJournalTrans.Due  = stagingTable.DueDate;

ledgerJournalTrans.DocumentDate = stagingTable.DocumentDate;

ledgerJournalTrans.ExchRate = stagingTable.ExchRate;

ledgerJournalTrans.ReportingCurrencyExchRate = stagingTable.ExchRate;

ledgerJournalTrans.Txt  = stagingTable.Description;

ledgerJournalTrans.AccountType = str2Enum(ledgerJournalACType,stagingTable.AccountTypeStr);//stagingTable.AccountType;

ledgerJournalTrans.modifiedField(fieldNum(LedgerJournalTrans, AccountType));

if (stagingTable.AccountTypeStr == "Ledger")

{

ledgerJournalTrans.LedgerDimension  = this.generateLedgerDimension(stagingTable.AccountDisplayValue,stagingTable.BU,'',stagingTable.BranchCode,stagingTable.EmployeeCode);

}

else if (stagingTable.AccountTypeStr == "Vendor")

{

ledgerJournalTrans.LedgerDimension  = LedgerDynamicAccountHelper::getDynamicAccountFromAccountNumber(stagingTable.AccountDisplayValue,LedgerJournalACType::Vend);// stagingTable.Account;

ledgerJournalTrans.PostingProfile   = vendParameters.PostingProfile;

}

else if (stagingTable.AccountTypeStr == "Customer")

{

ledgerJournalTrans.LedgerDimension  = LedgerDynamicAccountHelper::getDynamicAccountFromAccountNumber(stagingTable.AccountDisplayValue,LedgerJournalACType::Cust);// stagingTable.Account;

ledgerJournalTrans.PostingProfile   = custParameters.PostingProfile;

}

else if (stagingTable.AccountTypeStr == "Bank")

{

ledgerJournalTrans.LedgerDimension  = LedgerDynamicAccountHelper::getDynamicAccountFromAccountNumber(stagingTable.AccountDisplayValue,LedgerJournalACType::Bank);// stagingTable.Account;

}

ledgerJournalTrans.modifiedField(fieldNum(LedgerJournalTrans, LedgerDimension));

ledgerJournalTrans.DefaultDimension = this.createDefaultDimension( stagingTable.LobCode,stagingTable.Department,stagingTable.BranchCode);

if (stagingTable.Credit)

{

ledgerJournalTrans.AmountCurCredit   = stagingTable.Credit;

ledgerJournalTrans.modifiedField(fieldNum(LedgerJournalTrans, AmountCurCredit));

}

else if (stagingTable.Debit)

{

ledgerJournalTrans.AmountCurDebit    = stagingTable.Debit;

ledgerJournalTrans.modifiedField(fieldNum(LedgerJournalTrans, AmountCurDebit));

}         

ledgerJournalTrans.OffsetAccountType      = LedgerJournalACType::Ledger;

ledgerJournalTrans.modifiedField(fieldNum(LedgerJournalTrans, OffsetAccountType));         

ledgerJournalTrans.insert();


How to create LedgerDimension in D365 F&O using x++

 The code below will help out to generate Ledger Dimension using x++ in D365 FO

   


 public RefRecId  generateLedgerDimension(MainAccountNum     _mainAccountId,

                                             DimensionValue     _businessUnit, 

                                             DimensionValue     _department,

                                             DimensionValue     _branchCode,

                                             DimensionValue     _employeeCode)

    {

        container                           conData;

        int                                 hierarchyCount;

        int                                 hierarchyIdx;

        LedgerRecId                         ledgerRecId;

        MainAccount                         mainAccount;

        RefRecId                            recordvalue;

        DimensionAttribute                  dimensionAttribute;

        DimensionAttributeValue             dimensionAttributeValue;

        DimensionSetSegmentName             DimensionSet;

        DimensionStorage                    dimStorage;

        DimensionAttributeValueContract     ValueContract;

        LedgerAccountContract               LedgerAccountContract;

        DimensionAttributeValueCombination  dimensionAttributeValueCombination;

        List                                valueContracts;


        #define.MainAccount('MainAccount')

        #define.Department('Department')

        #define.BusinessUnit('BusinessUnit')

        #define.BranchCode('BranchCode')

        #define.EmployeeCode('EmployeeCode')

        



        LedgerAccountContract   = new LedgerAccountContract();

        valueContracts          = new List(Types::Class);


        conData = [_mainAccountId];



        mainAccount     =   MainAccount::findByMainAccountId(_mainAccountId);

        recordvalue     =   DimensionHierarchy::getAccountStructure(mainAccount.RecId,Ledger::current());

        hierarchyCount  =   DimensionHierarchy::getLevelCount(recordvalue);

        DimensionSet    =   DimensionHierarchyLevel::getDimensionHierarchyLevelNames(recordvalue);


        if (recordvalue)

        {

            for(hierarchyIdx = 1;hierarchyIdx<=hierarchyCount;hierarchyIdx++)

            {

                if(hierarchyIdx == 1)

                {

                    continue;

                }

                dimensionAttribute = DimensionAttribute::findByLocalizedName(DimensionSet[hierarchyIdx],false);

                                

                if (dimensionAttribute.Name == #Department)

                {

                    conData += [_department];

                }

                else if (dimensionAttribute.Name == #BusinessUnit)

                {

                    conData += [_businessUnit];

                }

                else if (dimensionAttribute.Name == #BranchCode)

                {

                    conData += [_branchCode];

                }

                else if (dimensionAttribute.Name == #EmployeeCode)

                {

                    conData += [_employeeCode];

                }



                if(dimensionAttribute)

                {

                    dimensionAttributeValue = DimensionAttributeValue::findByDimensionAttributeAndValue(dimensionAttribute,conPeek(conData,hierarchyIdx));


                    if(dimensionAttributeValue)

                    {

                        ValueContract = new DimensionAttributeValueContract();

                        ValueContract.parmName(dimensionAttribute.Name) ;

                        ValueContract.parmValue(dimensionAttributeValue.CachedDisplayValue);

                        valueContracts.addEnd(ValueContract);

                    }

                }


            }

            LedgerAccountContract.parmMainAccount(_mainAccountId);

            LedgerAccountContract.parmValues(valueContracts);


            dimStorage = DimensionServiceProvider::buildDimensionStorageForLedgerAccount(LedgerAccountContract);

            dimensionAttributeValueCombination = DimensionAttributeValueCombination::find(dimStorage.save());

            ledgerRecId = dimensionAttributeValueCombination.RecId;

        }

        else

        {

            warning (strfmt('Ledger dimension Issue for main account %1', _mainAccountId));

        }


        return  ledgerRecId;

    }