Thursday 16 December 2021

Getting error while doing Database Sync

Getting error while performing DB sync and Dynamics 365 Unified Operations: Batch management services not getting started


Hi,

Getting the below error while doing full DB Sync and Batch services also not getting started. Have done a full build with reference package and it has passed without an error. Its D365 Finance & Ops PU45 environment:

DB Sync error:

at Microsoft.Dynamics.Ax.MSIL.Interop.createSession(String userId, String company, String language, String partitionKey, Int32 workLoadType)
at Microsoft.Dynamics.AX.Framework.Database.Tools.SyncEngine.Run(String metadataDirectory, String sqlConnectionString, SyncOptions options)
at Microsoft.Dynamics.Ax.MSIL.Interop.throwException(Int32 ExceptionValue, interpret* ip)
at Microsoft.Dynamics.Ax.MSIL.Interop.createRunAsSession(String userId, String company, String language, String partitionKey, WorkLoadTypeEnum workLoadType)
at Microsoft.Dynamics.AX.Framework.Database.Tools.SyncEngine.FullTableViewSync()
at Microsoft.Dynamics.AX.Framework.Database.Tools.SyncEngine.RunSync()
Microsoft.Dynamics.Ax.Xpp.ErrorException: Failed to create a session; confirm that the user has the proper privileges to log on to Microsoft Dynamics 365 for Finance and Operations.
at Microsoft.Dynamics.AX.Framework.Database.Tools.SyncEngine.FullSync()
at Microsoft.Dynamics.Ax.MSIL.XppSession.Start(String userId, String company, String language, String partitionKey, Int32 workLoadType)
More Information: Invalid partitionKey parameter arguments passed to the method globalUserGuidCache::getUserGuid
at Microsoft.Dynamics.Ax.MSIL.XppSession.Start()
at Microsoft.Dynamics.AX.Framework.Database.Tools.LegacyCodepath.RunFullTableSync(SyncOptions syncOptions, String sqlConnectionString, IMetadataProvider metadataProvider)
Unhandled exception
at Microsoft.Dynamics.AX.Framework.Database.Tools.LegacyCodepath.ExecuteWithinAOS(SyncOptions syncOptions, String sqlConnectionString, IMetadataProvider metadataProvider, Func`1 func, Action`1 errorHandler)
syncengine.exe exited with code -1.

Resolution:

After some checks on the DB, I found out that the sysglobalconfiguration was set to azuresql even though I was working with a local sql server. Reason behind, DB backup has been taken from Tier-2 environment and restored into Dev box which is Tier-1 environment so after changing the configuration the DB Sync was succeeded and Batch management service gets started.

Before:











After (fix through sql query):

update sysglobalconfiguration set  value = 'SQLSERVER' where RecId = 5637144576

 

Happy Daxing...

Friday 26 November 2021

To post Packing slip through X++ in D365 F&O

Post Packing slip in D365 F&O

Today, I am going to share with you about how to post Packing slip for entire sales order or a particular sales order line. 

Here we go for entire Sales order:-

Post Packing slip for a sales order

To post Packing slip for an entire sales order, we need to pass SalesId to initialize data into Sales Parm tables.

/// <summary>
    /// to post Packing slip for an item requirement.
    /// </summary>
    /// <param name = "_salesId"> SalesId is passed as parameter. </param>
    /// <param name = "_inventTransId"> InventTransId is passed as parameter. </param>
    public void postPackingSlip(SalesId _salesId, InventTransId _inventTransId)
    {
        SalesTable      salesTable = salesTable::find(_salesId);
        SalesFormLetter salesFormLetter;
        SalesParmLine   salesParmLine;
    
        // below line is to post Packing slip for a sales order
        salesFormLetter = SalesFormLetter::construct(DocumentStatus::PackingSlip);

        // If you want to post Packing slip for a project sales order, comment above line and uncomment below line
        //salesFormLetter = SalesFormLetter::construct(DocumentStatus::ProjectPackingSlip);

        // Do the steps manually, which normally are done in method
        salesFormLetter.salesTable(salesTable);
        salesFormLetter.initParmSalesTable(salesFormLetter.salesTable());
              salesFormLetter.transDate(DateTimeUtil::getSystemDate(DateTimeUtil::getUserPreferredTimeZone()));
        salesFormLetter.specQty(SalesUpdate::All);
        salesFormLetter.proforma(NoYes::No);        //salesFormLetter.salesParmUpdate().Proforma);
        salesFormLetter.printFormLetter(NoYes::No); //salesFormLetter.printFormLetter());
        salesFormLetter.printCODLabel(NoYes::No);
        salesFormLetter.printShippingLabel(NoYes::No);
        salesFormLetter.usePrintManagement(false);
        salesFormLetter.creditRemaining(salesFormLetter.creditRemaining());
        salesFormLetter.createParmUpdateFromParmUpdateRecord(SalesFormletterParmData::initSalesParmUpdateFormletter(salesFormLetter.documentStatus(),
                                                                                                                    salesFormLetter.pack(),
                                                                                                                    true,
                                                                                                                    false,
                                                                                                                    false));
        salesFormLetter.initParameters(salesFormLetter.salesParmUpdate(), Printout::Current);
        salesFormLetter.initLinesQuery();        
        salesFormLetter.run();     
    }

Post Packing slip for a sales order line (for a particular lot)

To post Packing slip for a particular lot (sales order line), we need to pass SalesId to initialize data into Sales Parm tables and then clear unwanted parm lines.

/// <summary>
    /// to post Packing slip for an item requirement.
    /// </summary>
    /// <param name = "_salesId"> SalesId is passed as parameter. </param>
    /// <param name = "_inventTransId"> InventTransId is passed as parameter. </param>
    public void postPackingSlip(SalesId _salesId, InventTransId _inventTransId)
    {
        SalesTable      salesTable = salesTable::find(_salesId);
        SalesFormLetter salesFormLetter;
        SalesParmLine   salesParmLine;
    
        // below line is to post Packing slip for a sales order
        salesFormLetter = SalesFormLetter::construct(DocumentStatus::PackingSlip);

        // If you want to post Packing slip for a project sales order, comment above line and uncomment below line
        //salesFormLetter = SalesFormLetter::construct(DocumentStatus::ProjectPackingSlip);

        // Do the steps manually, which normally are done in method
        salesFormLetter.salesTable(salesTable);
        salesFormLetter.initParmSalesTable(salesFormLetter.salesTable());
        salesFormLetter.transDate(DateTimeUtil::getSystemDate(DateTimeUtil::getUserPreferredTimeZone()));
        salesFormLetter.specQty(SalesUpdate::All);
        salesFormLetter.proforma(NoYes::No);        //salesFormLetter.salesParmUpdate().Proforma);
        salesFormLetter.printFormLetter(NoYes::No); //salesFormLetter.printFormLetter());
        salesFormLetter.printCODLabel(NoYes::No);
        salesFormLetter.printShippingLabel(NoYes::No);
        salesFormLetter.usePrintManagement(false);
        salesFormLetter.creditRemaining(salesFormLetter.creditRemaining());
        salesFormLetter.createParmUpdateFromParmUpdateRecord(SalesFormletterParmData::initSalesParmUpdateFormletter(salesFormLetter.documentStatus(),
                                                                                                                    salesFormLetter.pack(),
                                                                                                                    true,
                                                                                                                    false,
                                                                                                                    false));
        salesFormLetter.initParameters(salesFormLetter.salesParmUpdate(), Printout::Current);
        salesFormLetter.initLinesQuery();

        // Delete unwanted records from SalesParmLine
        while select forupdate salesParmLine
                where salesParmLine.ParmId == salesFormLetter.parmId()
        {
            if (salesParmLine.InventTransId != _inventTransId)
            {
                salesParmLine.delete();
            }
        }
        salesFormLetter.run();     
    }



Happy Daxing...

To Register pick list through X++ in D365 F&O

Register Pick list in D365 F&O

Today, I am going to share with you about how to register pick list for entire sales order or a particular sales order line. 

Here we go for entire Sales order:-

Register pick list for a sales order

To register pick list for an entire sales order, we need to pass SalesId to initialize data into Sales Parm tables. Here, we are passing InventDimId from InventTrans table (you can pick InventDimId from your expected source as well).

    /// <summary>
    /// to register pick list for an item requirement.
    /// </summary>
    /// <param name = "_inventTransId"> InventTransId is passed as parameter. </param>
    /// <param name = "_inventDimId"> InventDimId is passed as parameter. </param>
    public void registerPickingList(SalesId _salesId, InventTransId _inventTransId, InventDimId _inventDimId)
    {
        SalesParmLine       salesParmLine;
        WMSPickingRoute     wmsPickingRoute;
        WMSOrderTrans       wmsOrderTrans;

        Map                 wmsOrderTransMap    = new Map(Types::Int64, Types::Enum);
        List                pickingRouteList    = new List(Types::String);

        select firstonly ParmId,PickingRouteID,transRefId,RecId from wmsPickingRoute
        join OrigSalesId,ParmId,InventTransId,RecId from salesParmLine
            where wmsPickingRoute.ParmId        == salesParmLine.ParmId
            &&    wmsPickingRoute.transRefId    == salesParmLine.OrigSalesId
            &&    salesParmLine.OrigSalesId     == _salesId
            &&    salesParmLine.InventTransId   == _inventTransId;
            
        ttsbegin;
        while select forupdate wmsOrderTrans
                where wmsOrderTrans.routeId == wmsPickingRoute.PickingRouteID
                &&    wmsOrderTrans.inventTransRefId == _salesId
                &&    wmsOrderTrans.inventTransType == inventTransType::Sales
                &&    wmsOrderTrans.fullPallet == 0
        {
            if (wmsOrderTrans.InventTransId == _inventTransId && _inventDimId != strMin())
            {
                wmsOrderTrans.inventDimId = _inventDimId;
                wmsOrderTrans.update();
            }
        }
        ttscommit;

        pickingRouteList.addEnd(wmsPickingRoute.PickingRouteID);

        WMSPickingRoute::finishMulti(pickingRouteList.pack());
    }

Register pick list for a sales order line (for a particular lot)

To register pick list for a particular lot (sales order line), we need to pass SalesId to initialize data into Sales Parm tables and then clear unwanted parm lines. Here, we are passing InventDimId from InventTrans table (you can pick InventDimId from your expected source as well).

    /// <summary>
    /// to register pick list for an item requirement.
    /// </summary>
    /// <param name = "_inventTransId"> InventTransId is passed as parameter. </param>
    /// <param name = "_inventDimId"> InventDimId is passed as parameter. </param>
    public void registerPickingList(SalesId _salesId, InventTransId _inventTransId, InventDimId _inventDimId)
    {
        SalesParmLine       salesParmLine;
        WMSPickingRoute     wmsPickingRoute;
        WMSOrderTrans       wmsOrderTrans;

        Map                 wmsOrderTransMap    = new Map(Types::Int64, Types::Enum);
        List                pickingRouteList    = new List(Types::String);

        select firstonly ParmId,PickingRouteID,transRefId,RecId from wmsPickingRoute
        join OrigSalesId,ParmId,InventTransId,RecId from salesParmLine
            where wmsPickingRoute.ParmId        == salesParmLine.ParmId
            &&    wmsPickingRoute.transRefId    == salesParmLine.OrigSalesId
            &&    salesParmLine.OrigSalesId     == _salesId
            &&    salesParmLine.InventTransId   == _inventTransId;
            
        ttsbegin;
        while select forupdate wmsOrderTrans
                where wmsOrderTrans.routeId == wmsPickingRoute.PickingRouteID
                &&    wmsOrderTrans.inventTransRefId == _salesId
                &&    wmsOrderTrans.inventTransType == inventTransType::Sales
                &&    wmsOrderTrans.fullPallet == 0
        {
            if (wmsOrderTrans.InventTransId == _inventTransId && _inventDimId != strMin())
            {
                wmsOrderTrans.inventDimId = _inventDimId;
                wmsOrderTrans.update();
            }
            else if (wmsOrderTrans.expeditionStatus == WMSExpeditionStatus::Activated ||
                        wmsOrderTrans.expeditionStatus == WMSExpeditionStatus::None)
            {
                wmsOrderTransMap.insert(wmsOrderTrans.RecId, wmsOrderTrans.expeditionStatus);

                wmsOrderTrans.cancel();
            }
        }
        ttscommit;

        pickingRouteList.addEnd(wmsPickingRoute.PickingRouteID);

        WMSPickingRoute::finishMulti(pickingRouteList.pack());

        MapEnumerator   wmsMapEnumerator = wmsOrderTransMap.getEnumerator();

        while (wmsMapEnumerator.moveNext())
        {
            wmsOrderTrans = null;
            ttsbegin;
            update_recordset wmsOrderTrans
                    setting expeditionStatus = wmsMapEnumerator.currentValue()
                    where wmsOrderTrans.RecId == wmsMapEnumerator.currentKey()
                    &&    wmsOrderTrans.routeId == wmsPickingRoute.PickingRouteID;
            ttscommit;
        }
    }



Happy Daxing...

To post Picking list through X++ in D365 F&O

Post Picking list in D365 F&O

Today, I am going to share with you about how to post Picking list for entire sales order or a particular sales order line. 

Here we go for entire Sales order:-

Post Picking list for a sales order

To post picking list for an entire sales order, we need to pass SalesId to initialize data into Sales Parm tables.

/// <summary>
    /// to post pick list for an item requirement.
    /// </summary>
    /// <param name = "_salesId"> SalesId is passed as parameter. </param>
    /// <param name = "_inventTransId"> InventTransId is passed as parameter. </param>
    public void postPickingList(SalesId _salesId, InventTransId _inventTransId)
    {
        SalesTable      salesTable = salesTable::find(_salesId);
        SalesFormLetter salesFormLetter;
        SalesParmLine   salesParmLine;
    
        // below line is to post Picking list for a sales order
        salesFormLetter = SalesFormLetter::construct(DocumentStatus::PickingList);

        // If you want to post Picking list for a project sales order, comment above line and uncomment below line
        //salesFormLetter = SalesFormLetter::construct(DocumentStatus::ProjectPickingList);

        // Do the steps manually, which normally are done in method
        salesFormLetter.salesTable(salesTable);
        salesFormLetter.initParmSalesTable(salesFormLetter.salesTable());
              salesFormLetter.transDate(DateTimeUtil::getSystemDate(DateTimeUtil::getUserPreferredTimeZone()));
        salesFormLetter.specQty(SalesUpdate::All);
        salesFormLetter.proforma(NoYes::No);        //salesFormLetter.salesParmUpdate().Proforma);
        salesFormLetter.printFormLetter(NoYes::No); //salesFormLetter.printFormLetter());
        salesFormLetter.printCODLabel(NoYes::No);
        salesFormLetter.printShippingLabel(NoYes::No);
        salesFormLetter.usePrintManagement(false);
        salesFormLetter.creditRemaining(salesFormLetter.creditRemaining());
        salesFormLetter.createParmUpdateFromParmUpdateRecord(SalesFormletterParmData::initSalesParmUpdateFormletter(salesFormLetter.documentStatus(),
                                                                                                                    salesFormLetter.pack(),
                                                                                                                    true,
                                                                                                                    false,
                                                                                                                    false));
        salesFormLetter.initParameters(salesFormLetter.salesParmUpdate(), Printout::Current);
        salesFormLetter.initLinesQuery();        
        salesFormLetter.run();     
    }

Post Picking list for a sales order line (for a particular lot)

To post picking list for a particular lot (sales order line), we need to pass SalesId to initialize data into Sales Parm tables and then clear unwanted parm lines.

/// <summary>
    /// to post pick list for an item requirement.
    /// </summary>
    /// <param name = "_salesId"> SalesId is passed as parameter. </param>
    /// <param name = "_inventTransId"> InventTransId is passed as parameter. </param>
    public void postPickingList(SalesId _salesId, InventTransId _inventTransId)
    {
        SalesTable      salesTable = salesTable::find(_salesId);
        SalesFormLetter salesFormLetter;
        SalesParmLine   salesParmLine;
    
        // below line is to post Picking list for a sales order
        salesFormLetter = SalesFormLetter::construct(DocumentStatus::PickingList);

        // If you want to post Picking list for a project sales order, comment above line and uncomment below line
        //salesFormLetter = SalesFormLetter::construct(DocumentStatus::ProjectPickingList);

        // Do the steps manually, which normally are done in method
        salesFormLetter.salesTable(salesTable);
        salesFormLetter.initParmSalesTable(salesFormLetter.salesTable());
        salesFormLetter.transDate(DateTimeUtil::getSystemDate(DateTimeUtil::getUserPreferredTimeZone()));
        salesFormLetter.specQty(SalesUpdate::All);
        salesFormLetter.proforma(NoYes::No);        //salesFormLetter.salesParmUpdate().Proforma);
        salesFormLetter.printFormLetter(NoYes::No); //salesFormLetter.printFormLetter());
        salesFormLetter.printCODLabel(NoYes::No);
        salesFormLetter.printShippingLabel(NoYes::No);
        salesFormLetter.usePrintManagement(false);
        salesFormLetter.creditRemaining(salesFormLetter.creditRemaining());
        salesFormLetter.createParmUpdateFromParmUpdateRecord(SalesFormletterParmData::initSalesParmUpdateFormletter(salesFormLetter.documentStatus(),
                                                                                                                    salesFormLetter.pack(),
                                                                                                                    true,
                                                                                                                    false,
                                                                                                                    false));
        salesFormLetter.initParameters(salesFormLetter.salesParmUpdate(), Printout::Current);
        salesFormLetter.initLinesQuery();

        // Delete unwanted records from SalesParmLine
        while select forupdate salesParmLine
                where salesParmLine.ParmId == salesFormLetter.parmId()
        {
            if (salesParmLine.InventTransId != _inventTransId)
            {
                salesParmLine.delete();
            }
        }
        salesFormLetter.run();     
    }


Happy Daxing...

Thursday 11 February 2021

Find and Delete workspace from TFS through Visual studio command prompt

 

How to delete workspace from TFS Visual Studio

In this blog, we will see how we can delete any of the TFS workspace which is assigned to different user


Even if tried to remove/delete the workspace from Visual Studio, We’re unable to map existing workspace to new user.  

In such scenario, It is necessary to delete the workspace explicitly while getting the error as below 

"The working folder 'Workspace_Folder_Local_Path' is already in use by  the workspace : on computer

Solution: 

1. Open Developer Command Prompt for VS2015 from Start menu Or, type the word Command in search box and select "VS2015 x86 x64 Cross Tools Command Prompt".


2. For getting the list of workspaces, run the below command
tf workspaces

3. For deleting a workspace, run the below command

Syntax: 

TF workspace /delete workspacename;owner_id

Real command: 

TF workspace /delete xxx-Dev-4;treddi




Happy DAXing...

Thursday 19 November 2020

Error message : More than one form was opened at once for the lookup control. in D365 FinOps

 Error message: More than one form was opened at once for the lookup control. in lookup in D365 F&O

If you get error message (More than one form was opened at once for the lookup control.) in notification Action Centre while clicking on lookup button in D365 then you just need to call a peace of code just after lookup performs.

        //cancel super() to prevent error.

        FormControlCancelableSuperEventArgs cancelEventArgs = e as FormControlCancelableSuperEventArgs;

        cancelEventArgs.CancelSuperCall();

For example - Lookup method:

[FormControlEventHandler(formControlStr(EcoResProductDetailsExtended, InventTable_HSNRFColorCode), FormControlEventType::Lookup)]

    public static void InventTable_HSNFRColorCode_OnLookup(FormControl sender, FormControlEventArgs e)

    {

        Query                   query = new Query();

        QueryBuildDataSource    queryBuildDataSource;

        SysTableLookup          sysTableLookup;


        sysTableLookup = SysTableLookup::newParameters(tableNum(HSNRFColorCodes), sender);

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

   

        sysTableLookup.addLookupField(fieldNum(HSNRFColorCodes, NRFColorCode));

        sysTableLookup.addLookupField(fieldNum(HSNRFColorCodes, NRFColorGroup));

        sysTableLookup.addLookupField(fieldNum(HSNRFColorCodes, Description));


        sysTableLookup.parmQuery(query);

        sysTableLookup.performFormLookup();

        // If you get error message (More than one form was opened at once for the lookup control.) in notification Action Centre then call below one line code.

        //cancel super() to prevent error.

        FormControlCancelableSuperEventArgs cancelEventArgs = e as FormControlCancelableSuperEventArgs;

        cancelEventArgs.CancelSuperCall();

    }


Happy DAXing...

Monday 9 November 2020

How to get ExecutionId at runtime in D365 FO Data management

 Get ExecutionId at runtime in Data entity.

Today, I am going to share a practice code to get ExecutionId at runtime in D365 FinOps.
Lets say, you are working on a data entity and you need to get ExecutionId at run time so that you can call you custom logic for that particular data job.

Check the data entity you are working on and find the 2- methods (1. postGetStagingData 2. postTargetProcess). If you don't find these methods, you can add in the Data entity extension.

Method-1:
// This is the method which executes before data execution from staging to target
public static void postGetStagingData(DMFDefinitionGroupExecution _dmfDefinitionGroupExecution)
{
        DMFExecutionId     executionId = _dmfDefinitionGroupExecution.ExecutionId;
}


Method-2:
// This is the method which executes after data execution in target at event of DMFEntityWritter.write()
public static void postTargetProcess(DMFDefinitionGroupExecution _dmfDefinitionGroupExecution)
{
        DMFExecutionId     executionId = _dmfDefinitionGroupExecution.ExecutionId;
}


Happy DAXing...

Wednesday 30 September 2020

Customize Sales Totals form in D365 FO (AX)

Customize Sales Totals form in D365 FO (AX)

Today, I am going to share with you that how to override Sales tax in Sales Totals form based on condition.

Here, my requirement is- If (SalesTable.HSChangedInD365 == NoYes::No) then Sales Totals form should display the tax what we have received through web service (Or, order import through from party API) otherwise it should display the standard calculated tax. So I have overridden the tax value when values are initializing in container.

Here, we go...

/// <summary>
///     this is the extension class of SalesTotals class.
/// </summary>
[ExtensionOf(classStr(SalesTotals))]
final class SalesTotalsLFI_Extension
{
    /// <summary>
    ///     this is the COC of SalesTotals.displayFieldsServer()
    ///     to modify container and initialize Total sales tax and updated Invoice amount based on below condition.
    /// </summary>
    /// <param name = "_orderTable">Caller table to calculate totals for.</param>
    /// <param name = "_specQty">Quantity to calculate totals for.</param>
    /// <param name = "_currencyCode">Currency code to calculate totals for.</param>
    /// <returns>
    /// Container of the field values to be shown on the totals form.
    /// </returns>
    static public container displayFieldsServer(Common _orderTable, SalesUpdate _specQty, CurrencyCode _currencyCode)
    {
        container       resultContainer = conNull();

        resultContainer = next displayFieldsServer(_orderTable, _specQty, _currencyCode);

        if (_orderTable.TableId == tableNum(SalesTable))
        {
            SalesTable  salesTableLoc = _orderTable;
            if (salesTableLoc.HSChangedInD365 == NoYes::No && conPeek(resultContainer, TradeTotals::posTaxTotal()) == 0)
            {
                resultContainer = conPoke(resultContainer, TradeTotals::posTaxTotal(), salesTableLoc.HSTotalWebTaxAmountCur);
                resultContainer = conPoke(resultContainer, TradeTotals::posTotalAmount(), (salesTableLoc.HSTotalWebTaxAmountCur + conpeek(resultContainer, TradeTotals::posTotalAmount())));
            }
        }

        return resultContainer;
    }

Happy DAXING...




Monday 28 September 2020

How to override (adjust) tax through X++ code in D365 FO

 Override the tax in D365 FinOps

Today, I am going to share some activities based on Sales tax on to "Override" and "Remove the adjusted tax amount" too for a particular Sales order. 

Task# 01: To override sales tax amount in a Sales order

Here, I am going to share my experience to override the sales tax (adjust sales tax) in a Sales order.

Scenario# 1.1: To override sales tax in a sales order

Here, you just need SalesTable buffer at runtime which you need to pass to the code. I have a custom field control in the SalesTable where I am storing total actual tax amount (which I am getting from the third-party tool). 

TaxRegulation   taxRegulation;
                    SalesTotals     salesTotals = SalesTotals::construct(salesTableLocal);
        
                    salesTotals.calc();
                    taxRegulation = TaxRegulation::newTaxRegulation(salesTotals.tax());
                    taxRegulation.allocateAmount(salesTableLocal.HSTotalWebTaxAmountCur);
                    taxRegulation.saveTaxRegulation();

Scenario# 1.2: To override sales tax in a sales order

Here, the scenario is to create a Sales order and post Sales invoice along with Tax override activity too.





Task# 02: To reset Actual to Calculated sales tax amount in a Sales order

Here, I am going to share my experience on to remove adjusted sales tax amount in a Sales order.

Scenario# 2.1: To override sales tax in a sales order

Here, you just need SalesTable buffer at runtime which you need to pass to the code.

// reset actual to calculated sales tax amount.
            TaxRegulation   taxRegulation;
            SalesTotals     salesTotals = SalesTotals::construct(salesTableLocal);
            salesTotals.calc();

            taxRegulation = TaxRegulation::newTaxRegulation(salesTotals.tax(), null,salesTableLocal.TableId, salesTableLocal.RecId);
            if (taxRegulation.taxLinesExist())
            {
                taxRegulation.resetTaxRegulation();

                taxRegulation.saveTaxRegulation();
            }



Happy DAXing...


Tuesday 15 September 2020

How to calculate total tax for a order in D365 using X++

 How to calculate total tax for a Sales order using X++

Today, I will show, how to calculate tax in D365 FO (AX) through code

Scenario 1: To find Sales Totals values for Sales order in D365

Here, my requirement is to export Sales Totals component's value through custom data entity. I have applied below logic, hope it will help you...

Accounts receivable/ All sales orders/ select any Sales order/ Sales order button group/ View/ Totals 
public void postLoad()
    {
        SalesLine   salesLineLoc;
        SalesTable  salesTableLoc = SalesTable::find(this.SalesId);
        SalesTotals totals;

        super();

        select sum(LineAmount) from salesLineLoc
            where salesLineLoc.SalesId == salesTableLoc.SalesId;
        this.SubTotalAmount = salesLineLoc.LineAmount;

        // No problem if the total is not negative
        totals = SalesTotals::construct(salesTableLoc, SalesUpdate::All);
        totals.calc();
        this.TotalEndDisc   = conPeek(totals.displayFields(), TradeTotals::posEndDisc());
        this.TotalLineDisc  = conPeek(totals.displayFields(), TradeTotals::posLineDisc());
        this.TotalCharges   = conPeek(totals.displayFields(), TradeTotals::posMarkup());
        this.InvoiceAmount  = conPeek(totals.displayFields(), TradeTotals::posTotalAmount());
        
        salesLineLoc = null;
        salesLineLoc = SalesLine::findInventTransId(this.InventTransId);
        if (salesTableLoc.HSChangedInD365 == NoYes::No)
        {
            this.WebShippingTaxAmount   = salesTableLoc.HSWebChargeTaxAmountCur;
            this.TotalTaxAmount         = salesTableLoc.HSTotalWebTaxAmountCur;
            this.LineTaxAmount          = salesLineLoc.HSWebLineTaxAmountCur;
        }
        else
        {
            this.TotalTaxAmount = conPeek(totals.displayFields(), TradeTotals::posTaxTotal());
            //this.TotalTaxAmount = abs(salesTotals.tax().totalTaxAmountCalculated());   // Total calculated sales tax amount
            //this.TotalTaxAmount = abs(salesTotals.tax().totalTaxAmount());             // Total actual sales tax amount

            totals.tax().sourceSingleLine(true, true);
            this.LineTaxAmount = totals.tax().totalRegulatedTaxAmountSingleLine(salesLineLoc.TableId, salesLineLoc.RecId);  // sales line actual tax amount
            //this.LineTaxAmount = salesTotals.tax().totalTaxAmountSingleLine(salesLineLoc.TableId, salesLineLoc.RecId);    // sales line calculated tax amount
            
            // to get calculated tax for a particular charge line associated with a sales order.
            MarkupTrans markupTransLoc;
            select firstonly markupTransLoc
                where markupTransLoc.MarkupCode == HSIntegrationParameters::find().PaidByCustomer  //'CUSTFRGHT'
                &&    markupTransLoc.TransRecId == salesTableLoc.RecId
                &&    markupTransLoc.TransTableId == tableNum(SalesTable);
            this.WebShippingTaxAmount = abs(totals.tax().taxPrLine(tableNum(MarkupTrans), markupTransLoc.RecId));
        }       
    }

//You have to pass these values to the functions whether it comes from PurchLine or SalesLine or any other table.
//This may not work for Avalara tax engine then follow below scenarios...

Tax::calcTaxAmount(purchLine.TaxGroup, purchLine.TaxItemGroup, Systemdateget(), purchLine.CurrencyCode, purchLine.LineAmount, TaxModuleType::Purch);

Scenario 2.1: To calculate tax for each Sales order line in D365

I have written display method in SalesLine table to show line-wise calculated sales tax. This method can be utilized in multiple places...

public display TaxAmountCur displayLineTaxAmount()
    {
        salesTable      salesTable;
        SalesTotals     salesTotals;
        TaxAmountCur    taxAmountCur;

        salesTable  = SalesTable::find(this.SalesId, false); 
        salesTotals = SalesTotals::construct(salesTable, SalesUpdate::All);
        salesTotals.calc();
        salesTotals.tax().sourceSingleLine(true, true);
        taxAmountCur    = salesTotals.tax().totalTaxAmountSingleLine(this.TableId, this.RecId);

        return TaxAmountCur;

    }

Scenario 2.2: To calculate tax for each Sales order line in D365

I have written display method in SalesLine table to show line-wise calculated sales tax. This method can be utilized in multiple places...


public display TaxAmount displayLineTaxAmount()
    {
        SalesLine       salesLineLoc;
       
select firstonly salesLineLoc
            where salesLineLoc.RecId == this.RecId;

        return Tax::calcTaxAmount(salesLineLoc.TaxGroup,
                                salesLineLoc.TaxItemGroup,
                                Systemdateget(), 
                                salesLineLoc.CurrencyCode,
                                salesLineLoc.LineAmount,
                                TaxModuleType::Sales);
    }

Scenario 3: To calculate tax for Sales order in D365

I have written display method in SalesTable table to show total calculated sales tax. This method can be utilized in multiple places...


public display TaxAmountCur displayTotalTaxAmount()
    {
        salesTable      salesTable;
        SalesTotals     salesTotals;
        TaxAmountCur    taxAmountCur;

        salesTable  = SalesTable::find(this.SalesId, false);
        salesTotals = SalesTotals::construct(salesTable, SalesUpdate::All);
        salesTotals.calc();
        taxAmountCur = abs(salesTotals.tax().totalTaxAmount());

        return TaxAmountCur;
    }

Add display method as a data field in Field groups in a table



ScenariScenario 4: To calculate tax for each Purchase order line in D365.

I have written display method in PurchLine table to show line-wise calculated purch tax. This method can be utilized in multiple places...

public display TaxAmountCur totalTaxAmount(boolean _adjustTaxSign = true, boolean _includeUseTax = false)
{
    PurchTotals     purchTotals;
    TaxAmountCur    taxAmountCur;

    purchTotals     = PurchTotals::newPurchTable(this.purchTable());
    purchTotals.calc();
    purchTotals.tax().sourceSingleLine(true, true);
    taxAmountCur    = purchTotals.tax().totalTaxAmountSingleLine(this.TableId, this.RecId, _adjustTaxSign, _includeUseTax);

    return taxAmountCur;
}

Scenario 5: To calculate tax for Purchase order in D365

I have written display method in PurchTable table to show total calculated purch tax. This method can be utilized in multiple places...

public display TaxAmountCur totalTaxAmount(boolean _adjustTaxSign = true, boolean _includeUseTax = false)
{
    PurchTotals     purchTotals;
    TaxAmountCur    taxAmountCur;

    purchTotals     = PurchTotals::newPurchTable(this.purchTable());
    purchTotals.calc();
    taxAmountCur    = purchTotals.tax().totalTaxAmount();

    return taxAmountCur;
}

Scenario 6: To calculate tax for GL journal in D365

I have written display method in LedgerJournalTrans table to show total calculated tax. This method can be utilized in multiple places...

public display TaxAmountCur totalTaxAmount(boolean _adjustTaxSign = true, boolean _includeUseTax = false)
{
        TaxCalculation taxCalculation;
    LedgerJournalTrans      ledgerJournalTrans;
    TmpTaxWorkTrans         tmpTaxWorkTrans;
    TaxAmountCur            taxAmountCur;

    ledgerJournalTrans = LedgerJournalTrans::findRecId(5637293082, false); 

    // This is from `\Classes\LedgerJournalEngine\getShowTax`
    taxCalculation = LedgerJournalTrans::getTaxInstance(ledgerJournalTrans.JournalNum, 
ledgerJournalTrans.Voucher,
  ledgerJournalTrans.Invoice, 
true, 
null, 
false, 
ledgerJournalTrans.TransDate);
    taxCalculation.sourceSingleLine(true, false);

    // This is from `\Classes\TaxTmpWorkTransForm\initTax`
    tmpTaxWorkTrans.setTmpData(taxCalculation.tmpTaxWorkTrans());

    // This is the temporary table that is populated
    while select tmpTaxWorkTrans
    {
        // This is from `\Classes\TaxTmpWorkTransForm\getSourceBaseAmountCur`
        taxAmountCur = (tmpTaxWorkTrans.SourceTaxAmountCur * tmpTaxWorkTrans.taxChangeDisplaySign(null));

        // This just outputs some data
        info(strFmt("%1: %2", tmpTaxWorkTrans.TaxCode, taxAmountCur));
    }

    return taxAmountCur;
}


--------------------------------
Happy DAXing...