Saturday, March 29, 2025

After product receipt from x++ code system is not creating project transactions

Resolving Project Transactions Issue in X++

Resolving Issue: Project Transactions Not Created After Product Receipt in X++

Introduction

When receiving a product receipt in Microsoft Dynamics 365 Finance and Operations via X++ code, you might encounter a scenario where project transactions are not generated, even when a valid project ID is associated with the purchase order. Debugging revealed that the system is not calling the runProjectPostings() method of the PurchFormLetter_PackingSlip class, which prevents the project packing slip from being posted. To resolve this, I created the runRemainProjectUpdates method in the code below.

Code Implementation

Product Receipt Creation


private TestPOReceiptResponseContract createProductReceipt(TestPOReceiptRequestContract _requestContract)
{
    PurchFormLetter purchFormLetter;
    PurchParmTable purchParmTable;
    PurchParmLine purchParmLine;
    PurchFormletterParmData purchFormLetterParmData;
    TestOrderLineRequestContract orderLineContract = _requestContract.parmOrderLineContract();
    PurchTable purchTable = PurchTable::find(orderLineContract.parmPurchaseOrderId());
    PurchLine purchLine = PurchLine::find(purchTable.PurchId, orderLineContract.parmLineNumber());

    purchFormLetterParmData = PurchFormletterParmData::newData(DocumentStatus::PackingSlip, VersioningUpdateType::Initial);
    purchFormLetterParmData.parmOnlyCreateParmUpdate(true);
    purchFormLetterParmData.createData(false);

    PurchParmUpdate purchParmUpdate = purchFormLetterParmData.parmParmUpdate();

    purchParmTable.clear();
    purchParmTable.TransDate = _requestContract.parmProducReceiptDate();
    purchParmTable.Ordering = DocumentStatus::PackingSlip;
    purchParmTable.ParmJobStatus = ParmJobStatus::Waiting;
    purchParmTable.Num = _requestContract.parmCoupaId();
    purchParmTable.PurchId = purchTable.PurchId;
    purchParmTable.insert();
    
    purchParmLine.initFromPurchLine(purchLine);
    purchParmLine.ReceiveNow = _requestContract.parmTotal();
    purchParmLine.ParmId = purchParmTable.ParmId;
    purchParmLine.insert();

    purchFormLetter = PurchFormLetter::construct(DocumentStatus::PackingSlip);
    purchFormLetter.transDate(_requestContract.parmProducReceiptDate());
    purchFormLetter.run();

    // Generate project item transactions
    if (purchTable.ProjId != '')
    {
        this.runRemainProjectUpdates(purchFormLetter, purchParmTable.ParmId);
    }
    
    return this.getResponseContract("@ENG_Labels:ProductReceiptCreated", true);
}
    

Explicitly Calling runRemainProjectUpdates to Create Project Transactions


public void runRemainProjectUpdates(PurchFormLetter _purchFormLetter, ParmId _parmId)
{
    FormletterOutputContract outputContract = _purchFormLetter.getOutputContract();
    VendPackingSlipVersion localVendPackingSlipVersion;
    VendPackingSlipJour localVendPackingSlipJour;
    VendPackingSlipTrans localVendPackingSlipTrans;
    PurchLine localPurchLine;
    SalesLine localSalesLine;
    SalesFormLetter salesFormLetter;

    if (ProjParameters::find().AutomaticItemConsumption == NoYes::Yes)
    {
        select firstonly RecId, AccountingDate from localVendPackingSlipVersion
            where localVendPackingSlipVersion.ParmId == _parmId
            exists join localVendPackingSlipJour
                where localVendPackingSlipVersion.VendPackingSlipJour == localVendPackingSlipJour.RecId
                exists join localVendPackingSlipTrans
                    where localVendPackingSlipTrans.VendPackingSlipJour == localVendPackingSlipJour.RecId
                    exists join localPurchLine
                        where localPurchLine.InventTransId == localVendPackingSlipTrans.InventTransId
                        && localPurchLine.ItemRefType == InventRefType::Sales
                        exists join localSalesLine
                            where localSalesLine.InventTransId == localPurchLine.InventRefTransId
                            && localSalesLine.ProjId;

        if (localVendPackingSlipVersion.RecId)
        {
            salesFormLetter = SalesFormLetter::newFromPurchFormLetter_PackingSlip(
                SysOperationHelper::base64Encode(outputContract.parmJournalLinesPacked()),
                DocumentStatus::ProjectPackingSlip);

            if (localVendPackingSlipVersion.AccountingDate)
            {
                salesFormLetter.transDate(localVendPackingSlipVersion.AccountingDate);
            }

            salesFormLetter.runOperation();
        }
    }
}
    

How to register return sales order lines using X++ code

How to Register Return Sales Order Lines in X++

The TestSalesReturnOrderLineRegister Class in X++

The TestSalesReturnOrderLineRegister class serves as an entry point to the product registration process, specialized for the sales return order process. It handles the return disposition code and manages sales return order specifics, such as creating inventory transactions.

Code Overview



class TestSalesReturnOrderLineRegister extends TradeOrderLineRegister
{
    ReturnDispositionCodeId returnDispositionCodeId;
    DialogField dialogDispositionCodeId;
    SalesLine salesLine;
    boolean reverseInventTrans;
    boolean recreateReservationLine;
    boolean inRegistration;

    public boolean init()
    {
        boolean ret = super();
        salesLine = salesPurchLine;
        return ret;
    }

    public void run()
    {
        this.runPreSuper();
        super();
        this.runPostSuper();
    }

    public void runPostSuper()
    {
        salesLine = SalesLine::findInventTransId(salesLine.InventTransId, true);
        if (inRegistration)
        {
            if (salesLine.ReturnStatus == ReturnStatusLine::Awaiting)
            {
                if (reverseInventTrans && !salesLine.ReturnAllowReservation)
                {
                    SalesLine::changeReturnOrderType(salesLine.InventTransId, null, true);
                    salesLine = SalesLine::findInventTransId(salesLine.InventTransId, true);
                }
                if (salesLine.ReturnDispositionCodeId)
                {
                    salesLine.ReturnDispositionCodeId = '';
                    salesLine.update();
                }
            }
        }
        else 
        {
            if (salesLine.ReturnStatus == ReturnStatusLine::Registered && recreateReservationLine)
            {
                salesLine.createReturnReservationLine();
                salesLine = SalesLine::findInventTransId(salesLine.InventTransId, true);
            }
        }
    }
}
    

Calling the TestSalesReturnOrderLineRegister Class


public void registerReturnSOLine(SalesId _salesId, ReturnDispositionCodeId _dispositionCode)
{
    TradeOrderLineRegister tradeOrderlineRegister;
    TestSalesReturnOrderLineRegister salesReturnOrderLineRegister;
    InventTransWMS_Register inventTransWMS_register;
    TmpInventTransWMS TmpInventTransWMS;
    SalesLine salesLine;
    InventTrans inventTrans;
    InventTransOrigin inventTransOrigin;
    returnDispositionCode returnDispositionCode;

    Args args = new Args();
    while select forUpdate salesLine where salesLine.SalesId == _returnSOTable.SalesId
    {
        args.record(salesLine);
        tradeOrderLineRegister = TestSalesReturnOrderLineRegister::construct();
        tradeOrderLineRegister.parmArgs(args);
        tradeOrderLineRegister.init();
        salesReturnOrderLineRegister = tradeOrderLineRegister;
        salesReturnOrderLineRegister.runPreSuper();
        
        select firstOnly returnDispositionCode
            where returnDispositionCode.DispositionAction == DispositionAction::Credit;
        
        salesLine.ReturnDispositionCodeId = returnDispositionCode.DispositionCodeId;
        salesLine.update();

        select firstOnly crossCompany inventTrans
            join RecId, InventTransId from inventTransOrigin
                where inventTransOrigin.InventTransId == salesLine.InventTransId
                && inventTrans.InventTransOrigin == inventTransOrigin.RecId;

        inventTransWMS_register = inventTransWMS_register::newStandard(tmpInventTransWMS);
        tmpInventTransWMS.clear();
        tmpInventTransWMS.initFromInventTrans(inventTrans);
        tmpInventTransWMS.InventDimId = inventTrans.InventDimId;
        tmpInventTransWMS.insert();

        inventTransWMS_register.writeTmpInventTransWMS(tmpInventTransWMS, inventTrans, inventTrans.inventDim());
        if (!inventTransWMS_register.updateInvent(salesLine))
        {
            throw error("Error during registration of sales line.");
        }
    }
}
    

Wednesday, March 26, 2025

How to create COC for form methods , How to get formRun , How to make control visible false on form in D365FO

How to create COC for form method in D365FO

How to create COC for form method in D365FO

Example Code

[ExtensionOf(formStr(ProjInvoiceListPage))]
final class TestProjInvoiceListPage_Extension
{
    public void init()
    {
        next init();

        FormRun formRun = this as FormRun;

        if(ProjParameters::find().TestPrintPGGDesignForProjectInvoice)
        {
            FormControl         stdViewCopyBtn   = formrun.design(0).controlName('PrintProjInvoiceCopyButton');
            FormControl         stdViewOrigBtn   = formrun.design(0).controlName('PrintProjInvoiceButton');
            stdViewCopyBtn.visible(false);
            stdViewOrigBtn.visible(false);
        }
        else
        {
            FormControl         customViewCopyBtn   = formrun.design(0).controlName('TestPSAProjInvoiceCopy');
            FormControl         customViewOrigBtn   = formrun.design(0).controlName('TestPSAProjInvoiceOriginal');

            customViewCopyBtn.visible(true);
            customViewOrigBtn.visible(true);
        }
    }
}

How to access extension class method on data entity field property

How to Use Extension Class Methods on Field Properties of Data Entity How to Use Extension Class Methods on Fie...