Monday, October 6, 2025

How to Control Edit Access for Sales Tax Fields in PR Lines Through Configuration in D365 Finance & Operations

 To provide a configuration-driven approach for controlling edit access to the Item Sales Tax Group and Sales Tax Group fields on Purchase Requisition (PR) lines.

When the toggle “Enable Tax Group configuration under PR lines” is enabled, users can edit these fields at specific PR workflow stages — in Draft and Finance Review states. When the toggle is disabled, editing is not allowed at any stage.

Configuration Steps:-
Navigation: Procurement and sourcing > Setup > Procurement and sourcing parameters > Configurations
Field: Purchase Requisition Line Sales Tax Group
Purpose: Controls whether users can edit the Item Sales Tax Group and Sales Tax Group fields on Purchase Requisition lines.
When Toggle Enabled (Yes): The Item Sales Tax Group and Sales Tax Group fields on PR lines are editable only when the PR status is Draft or Finance Review.

Code:-

[ExtensionOf(formDataSourceStr(PurchReqTable, PurchReqLine))]
internal final class PurchReqTable_PurchReqLine_Extension
{
    public int active()
    {
        FormDataSource  formDataSource = this as formDataSource;
        int ret = next active();

        PurchReqTable       purchreqTable;
        HcmWorker           hcmWorker;
        PurchReqLine        purchreqline = formDataSource.cursor();
        PurchParameters     purchParameters = PurchParameters::find();
        DirPersonUser       personUser;
        UserInfo            userInfo;
        str                 userId;
        UserGroupList       userGroupList;

        if (purchParameters.prlinesalestaxitemgroup == NoYes::Yes)
        {
            select purchreqTable
                where purchreqTable.RecId == purchreqline.purchReqTable;

            select hcmWorker
                where hcmWorker.RecId == purchreqline.Requisitioner;

            select personUser
                where personUser.PersonParty == hcmWorker.Person;

            select * from userInfo
                where userInfo.id == personUser.User;

            // Editable when PR is in Draft status and created by current user
            if (userInfo.id == curUserId() && purchreqTable.PurchReqStatus == "Draft")
            {
                formDataSource.allowEdit(true);
                formDataSource.object(fieldNum(PurchReqLine, TaxItemGroup)).enabled(true);
                formDataSource.object(fieldNum(PurchReqLine, TaxItemGroup)).allowEdit(true);
                formDataSource.object(fieldNum(PurchReqLine, TaxGroup)).enabled(true);
                formDataSource.object(fieldNum(PurchReqLine, TaxGroup)).allowEdit(true);
            }

            // For Finance Review stage
            WorkflowTrackingStatusTable workflowTrackingStatusTable;
            WorkflowWorkItemTable       workflowWorkItemTable;
            UserInfo                    userInfoloc;
            boolean                     isCurUser;

            FormTabPageControl tabItem                = this.formRun().design().controlName("tabItem") as FormTabPageControl;
            FormTabPageControl tabGeneralLine         = this.formRun().design().controlName("tabGeneralLine") as FormTabPageControl;
            FormTabPageControl tabProject             = this.formRun().design().controlName("tabProject") as FormTabPageControl;
            FormTabPageControl tabQuestionnaire       = this.formRun().design().controlName("tabQuestionnaire") as FormTabPageControl;
            FormTabPageControl tabFixedAsset          = this.formRun().design().controlName("tabFixedAsset") as FormTabPageControl;
            FormTabPageControl tabInventoryDimensions = this.formRun().design().controlName("tabInventoryDimensions") as FormTabPageControl;
            FormTabPageControl tabFinancialDimensions = this.formRun().design().controlName("tabFinancialDimensions") as FormTabPageControl;
            FormTabPageControl tabAddress             = this.formRun().design().controlName("tabAddress") as FormTabPageControl;

            if (purchreqTable.PurchReqStatus == "Finance Review")
            {
                while select workflowWorkItemTable
                    where workflowWorkItemTable.Type == WorkflowWorkItemType::WorkItem
                        && workflowWorkItemTable.Status == WorkflowWorkItemStatus::Pending
                    join workflowTrackingStatusTable
                        where workflowWorkItemTable.CorrelationId == workflowTrackingStatusTable.CorrelationId
                            && workflowTrackingStatusTable.ContextTableId == purchreqTable.TableId
                            && workflowTrackingStatusTable.ContextRecId == purchreqTable.RecId
                            && workflowTrackingStatusTable.TrackingStatus == WorkflowTrackingStatus::Pending
                    join userInfoloc
                        where workflowWorkItemTable.UserId == userInfoloc.id
                {
                    if (userInfoloc.id == curUserId())
                    {
                        isCurUser = true;
                        break;
                    }
                }

                if (isCurUser)
                {
                    formDataSource.allowEdit(true);
                    formDataSource.object(fieldNum(PurchReqLine, TaxItemGroup)).enabled(true);
                    formDataSource.object(fieldNum(PurchReqLine, TaxItemGroup)).allowEdit(true);
                    formDataSource.object(fieldNum(PurchReqLine, TaxGroup)).enabled(true);
                    formDataSource.object(fieldNum(PurchReqLine, TaxGroup)).allowEdit(true);

                    formDataSource.object(fieldNum(PurchReqLine, PurchPrice)).allowEdit(false);
                    formDataSource.object(fieldNum(PurchReqLine, CurrencyCode)).allowEdit(false);
                    formDataSource.object(fieldNum(PurchReqLine, IsPrepayment)).allowEdit(false);
                    formDataSource.object(fieldNum(PurchReqLine, LineComplete)).allowEdit(false);

                    tabItem.allowEdit(false);
                    tabGeneralLine.allowEdit(false);
                    tabProject.allowEdit(false);
                    tabQuestionnaire.allowEdit(false);
                    tabFixedAsset.allowEdit(false);
                    tabInventoryDimensions.allowEdit(false);
                    tabFinancialDimensions.allowEdit(false);
                    tabAddress.allowEdit(false);
                }
            }
        }

        return ret;
    }
}











Tuesday, September 23, 2025

Automating Consolidate Account Updates for New Voucher Transactions in D365 Finance and Operations

 Ensure the Consolidate Account field is automatically updated for all new voucher transactions based on the Consolidate MainAccount setup, to maintain consistency in financial consolidation and reporting.

[ExtensionOf(classStr(LedgerVoucherTransObject))]

internal final class LedgerVoucherTransObject_Extension

{

    public void initFromLedgerPostingTransaction(

        LedgerPostingTransactionTmp _ledgerPostingTransaction,

        LedgerPostingTransactionProjectTmp _projectPostingTransaction)

    {

        next initFromLedgerPostingTransaction(_ledgerPostingTransaction, _projectPostingTransaction);


        MainAccountConsolidateAccount   consolidationAccount;

        LedgerParameters                ledgerParameters = ledgerParameters::find();


        RecId mainAccount = MainAccount::findByLedgerDimension(generalJournalAccountEntry.ledgerDimension).RecId;


        select firstonly consolidationAccount

            where consolidationAccount.MainAccount == mainAccount

                && consolidationAccount.ConsolidateAccountGroup == ledgerParameters.ConsolidateAccountGroup;


        generalJournalAccountEntry.ConsolidateAccountName   = consolidationAccount.Name;

        generalJournalAccountEntry.ConsolidationMainAccount = consolidationAccount.ConsolidationMainAccount;

    }

}

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

[ExtensionOf(classStr(LedgerVoucherTransObject))]

internal final class LedgerVoucherTransObject_Extension

{

    public void initFromLedgerPostingTransaction(

        LedgerPostingTransactionTmp _ledgerPostingTransaction,

        LedgerPostingTransactionProjectTmp _projectPostingTransaction)

    {

        next initFromLedgerPostingTransaction(_ledgerPostingTransaction, _projectPostingTransaction);


        MainAccountConsolidateAccount consolidationAccount;

        LedgerParameters              ledgerParameters = ledgerParameters::find();


        RecId mainAccount = MainAccount::findByLedgerDimension(generalJournalAccountEntry.ledgerDimension).RecId;


        select firstonly consolidationAccount

            where consolidationAccount.MainAccount == mainAccount

                && consolidationAccount.ConsolidateAccountGroup == ledgerParameters.ConsolidateAccountGroup;


        generalJournalAccountEntry.ConsolidateAccountName   = consolidationAccount.Name;

        generalJournalAccountEntry.ConsolidationMainAccount = consolidationAccount.ConsolidationMainAccount;

    }

}

Update Consolidate MainAccount for old records

 Update existing records to ensure the Consolidation Account field is correctly populated based on the Consolidate MainAccount setup.

1. Contract Class:-

[DataContractAttribute]

[SysOperationAlwaysInitializeAttribute]

internal final class UpdateConsolidateAccountContract 

    implements SysOperationValidatable, SysOperationInitializable

{

    TransDate fromDate;


    [DataMemberAttribute,

     SysOperationLabel(literalStr("Transaction date"))]

    public TransDate parmFromDate(TransDate _fromDate = fromDate)

    {

        fromDate = _fromDate;

        return fromDate;

    }


    public boolean validate()

    {

        boolean isValid = true;


        if (!fromDate)

        {

            isValid = checkFailed("Date must be filled in.");

        }


        return isValid;

    }


    public void initialize()

    {

        fromDate = mkDate(1, 1, 2025);

    }

}


2. Service Class:-

internal final class UpdateConsolidateAccountService extends SysOperationServiceBase

{

    public void processOperation(UpdateConsolidateAccountContract contract)

    {

        GeneralJournalAccountEntry      generalJournalAccountEntry;

        GeneralJournalEntry             generalJournalEntry;

        MainAccountConsolidateAccount   consolidationAccount;

        LedgerParameters                ledgerParameters;

        TransDate                       transactionDate = contract.parmFromDate();


        select ledgerParameters

            where ledgerParameters.DataAreaId == curExt();

        

        if (ledgerParameters.ConsolidateAccountGroup)

        {

            while select forupdate generalJournalAccountEntry

                join generalJournalEntry

                    where generalJournalAccountEntry.GeneralJournalEntry == generalJournalEntry.RecId

                       && generalJournalAccountEntry.ConsolidationMainAccount == ""

                       && generalJournalEntry.SubledgerVoucherDataAreaId == curExt()

                       && generalJournalEntry.AccountingDate >= transactionDate

            {

                try

                {

                    select firstonly consolidationAccount

                        where consolidationAccount.MainAccount == generalJournalAccountEntry.MainAccount

                           && consolidationAccount.ConsolidateAccountGroup == ledgerParameters.ConsolidateAccountGroup;

                

                    if (consolidationAccount)

                    {

                        ttsBegin;

                        generalJournalAccountEntry.ConsolidationMainAccount = consolidationAccount.ConsolidationMainAccount;

                        generalJournalAccountEntry.ConsolidateAccountName   = consolidationAccount.Name;

                        generalJournalAccountEntry.doUpdate();

                        ttsCommit;

                    }

                }

                catch

                {

                    error(UpdateConsolidateAccountService::getError());

                    continue;

                }

            }

        }

    }


    public static str getError()

    {

        SysInfologEnumerator      sysInfologEnumerator;

        SysInfologMessageStruct   infoMessageStruct;

        str                       logMessage;

        str                       logString;

        int                       i;


        #Define.NewLine('\n')

        sysInfologEnumerator = SysInfologEnumerator::newData(infolog.infologData());


        while (sysInfologEnumerator.moveNext())

        {

            i = 1;

            if (logMessage)

            {

                logMessage += #NewLine;

            }

            infoMessageStruct = SysInfologMessageStruct::construct(sysInfologEnumerator.currentMessage());

            while (i <= infoMessageStruct.prefixDepth())

            {

                logString += infoMessageStruct.preFixTextElement(i) + '. ';

                i++;

            }

            logString += infoMessageStruct.message();

            logMessage += infoMessageStruct.message();

        }


        infolog().clear();

        return logMessage;

    }

}


3. controller class:-

internal final class UpdateConsolidateAccountController extends SysOperationServiceController

{

    public void new()

    {

        super();

        this.parmClassName(classStr(UpdateConsolidateAccountService));

        this.parmMethodName(methodStr(UpdateConsolidateAccountService, processOperation));

        this.parmDialogCaption("Update consolidate main account");

        // this.parmExecutionMode(SysOperationExecutionMode::ScheduledBatch);

    }


    public ClassDescription caption()

    {

        return "Update consolidate main account";

    }


    public static void main(Args args)

    {

        UpdateConsolidateAccountController controller = new UpdateConsolidateAccountController();

        controller.parmInBatch(true);

        controller.parmShowDialog(true);

        controller.startOperation();

    }

}

How to update column Based on joins in sql

 How to update column Based on joins in sql :-


UPDATE GeneralJournalAccountEntry

SET IS_ConsolidationMainAccount = '',

 IS_ConsolidateAccountName = ''

FROM GeneralJournalAccountEntry AS GJAE

 JOIN GeneralJournalEntry AS GJE

    ON GJE.RecId = GJAE.GeneralJournalEntry

WHERE GJE.SubledgerVoucherDataAreaId = 'VN06'

and GJE.ACCOUNTINGDATE > '2025-01-01'

Tuesday, September 2, 2025

How to Retrieve the Purchase Order Number in the LedgerTransAccount Form using X++ in D365 Finance & Operations

 How to Retrieve the Purchase Order Number in the LedgerTransAccount Form using X++ in D365 Finance & Operations

[ExtensionOf(tableStr(GeneralJournalEntry ))]

internal final class GeneralJournalEntryTable_Extension

{

 public display Name Ponumber()

    {

        Name                    ret;  

        VendInvoiceJour         VendInvoiceJour;

        VendTrans               VendTrans;

        VENDPACKINGSLIPVERSION  VENDPACKINGSLIPVERSION;

        VENDPACKINGSLIPJOUR     VENDPACKINGSLIPJOUR;


        select VendTrans

            where VendTrans.Voucher == this.SubledgerVoucher;


        select VendInvoiceJour where VendInvoiceJour.LedgerVoucher ==VendTrans.Voucher

            &&  VendInvoiceJour.InvoiceAccount ==VendTrans.AccountNum

            &&  VendInvoiceJour.InvoiceDate ==VendTrans.TransDate;

        if(VendInvoiceJour)

        {

            ret = VendInvoiceJour.PurchId;

        }

        

        SELECT VENDPACKINGSLIPJOUR FROM VENDPACKINGSLIPVERSION

            WHERE VENDPACKINGSLIPVERSION.LEDGERVOUCHER ==this.SubledgerVoucher;


        SELECT PACKINGSLIPId FROM VENDPACKINGSLIPJOUR

            WHERE VENDPACKINGSLIPJOUR.RECID == VENDPACKINGSLIPVERSION.VENDPACKINGSLIPJOUR;

        if(VENDPACKINGSLIPJOUR)

        {

            ret = VENDPACKINGSLIPJOUr.purchid;

        }


        return ret;

    }

}

If a How to get Purchase Order (PO) is linked to a Purchase Requisition (PR), fetch the Name from the PR and populate it in the Description field of the Product Receipt form.

 If a Purchase Order (PO) is linked to a Purchase Requisition (PR), fetch the Name from the PR and populate it in the Description field of the Product Receipt form.


[ExtensionOf(formDataSourceStr(PurchEditLines,PurchParmTable))]

internal final class PurchParmTableFormDataSource_Extension

{

    public void executeQuery()

    {

        next executeQuery();

        PurchReqTable purchReqTable;

        PurchReqLine  purchReqLine;

        FormStringControl   Invoicedescription = this.formRun().design().controlName("PurchParmTable_Description");

        PurchParmTable purchParmTable = this.cursor();

 

  

        select firstOnly purchReqLine

            where purchReqLine.PurchId == purchParmTable.PurchId

            && purchReqLine.INVENTDIMIDDATAAREA == purchParmTable.DataAreaId;

           

        if (purchReqLine.RecId)

        {

       

            select firstOnly purchReqTable

                where purchReqTable.RecId == purchReqLine.PurchReqTable;

 

            if (purchReqTable.RecId)

            {

               

                purchParmTable.Description = purchReqTable.PurchReqName;

                Invoicedescription.allowEdit(false);


            }

        }

        else

        {

            

            FormStringControl descriptionCtrl =  this.formRun().design().controlName('PurchParmTable_Description'); // Control name in the form

               

            if (descriptionCtrl)

            {

                descriptionCtrl.mandatory(true);

            }

        }

    

 

 

    }


}

Tuesday, June 3, 2025

How to find country region code in D365 fo X++

 How to find the country region code in D365 for X++

LogisticsAddressCountryRegion::findByISOCode(SysCountryRegionCode::countryInfo(curext())).CountryRegionId



How to Control Edit Access for Sales Tax Fields in PR Lines Through Configuration in D365 Finance & Operations

 To provide a configuration-driven approach for controlling edit access to the Item Sales Tax Group and Sales Tax Group fields on Purchase...