Wednesday, November 27, 2019

SQL "IN" Operator

Microsoft Introduce IN Operator in X++ Syntax, but it will work with Enums  only.

Following are the example how you can use this

SalesTable  salesTable;
container   con = [SalesType::Sales,
SalesType::ReturnItem,
SalesType::Subscription];

while select SalesId from salesTable
where salesTable.SalesType in con
{
Info(salesTable.SalesId);
}

Update AX system fields either CreatedDateTime or ModifiedDateTime

Sometimes while creating a record if we need to update the created date time or modified date time system field in D365FO. Please find the below code to update the createddatetime field during the record creation.

This method will help you to update createDateTime field while inserting the record.

static void updateCreatedDateTime(Args _args)
{
CustGroup custGroup;

ttsBegin;
new OverwriteSystemfieldsPermission().assert();
custGroup.overwriteSystemfields(true);
custGroup.custGroupId= “Abcd”;
custGroup.Name= “Abcd”;
custGroup.(fieldnum(custGroup,CreatedDateTime))= str2datetime( “2019/11/28 05:30:00” ,321 );
custGroup.doInsert();
custGroup.overwriteSystemfields(false);
CodeAccessPermission::revertAssert();
ttsCommit;
}


static void updateCreatedDateTime(Args _args)
{
        CustGroup custGroup;

ttsBegin;
new OverwriteSystemfieldsPermission().assert();
custGroup.overwriteSystemfields(true);
custGroup = custGroup::find("Abcd");
custGroup.Name= “Abcd”;
custGroup.(fieldnum(custGroup,ModifiedDateTime))= str2datetime( “2019/11/28 06:30:00” ,321 );
custGroup.doUpdate();
custGroup.overwriteSystemfields(false);
CodeAccessPermission::revertAssert();
ttsCommit;
}

Tuesday, November 26, 2019

UnitOfMeasureConverter::convert() method is depreciated

Hi All,

In new D365FO the UnitOfMeasureConverter::convert() method is depreciated so we need to use EcoResProductUnitConverter method for unit conversion.

Please refer below code, the EcoResProductUnitConverter::convertGivenUnitSymbolsForReleasedProduct method will convert the quantity from invent quantity to sales quantity

public PurchQty convertSalesQty(ItemId _itemId,InventDimId _inventDimId, Qty _qty, SalesUnit _salesUnit)
    {
        SalesQty        salesQty;
        InventUnitId    inventUnit;

        inventUnit  = InventTableModule::find(_itemId,ModuleInventPurchSales::Invent).UnitId;
       

         //salesQty    = UnitOfMeasureConverter::convert(_qty
        //                                            , UnitOfMeasure::unitOfMeasureIdBySymbol(inventUnit)
        //                                            , UnitOfMeasure::unitOfMeasureIdBySymbol(_salesUnit)
        //                                            , NoYes::Yes
        //                                            , InventTable::itemProduct(_itemId));

In AX2012 we used UnitOfMeasureConverter::convert() method to convert the unit from invent to sales. But if we use same method in D365Fo we will receive the best practices error like the method UnitOfMeasureConverter::convert() is depreciated use EcoResProductUnitConverter class.

Please find below how to use EcoResProductUnitConverter class in D365FO to convert unit.

        salesQty = EcoResProductUnitConverter::convertGivenUnitSymbolsForReleasedProduct(_itemId
                                                                                        , _inventDimId
                                                                                        , _qty
                                                                                        , inventUnit
                                                                                        , _salesUnit
                                                                                        , NoYes::Yes
                                                                                        , NoYes::No);

        return salesQty;
    }

//Refer smmSalesCustItemStatisticsDP class

EcoResProductUnitConverter productUnitConverter = EcoResProductUnitConverter::newGivenUnitSymbolsForReleasedProduct(
                                                                                                _itemId, 
                                                                                                _inventDimId, 
                                                                                                _unitFrom,
                                                                                                unitInvent, 
                                                                                                NoYes::Yes);

Tuesday, November 19, 2019

Transfer order picking list registration

Below code will help you to register picking list for transfer order

public void registerPickingRoute(PickingRouteId _pickingRouteId)
{
            pickingRoute = WMSPickingRoute::find(_pickingRouteId,true);
            if(pickingRoute)
            {
                pickingRoute.finish();
            }
}

D365FO Transfer order picking list registration with split quantity

During picking list registration in Transfer order we may need to split the lines for batch number or serial number registration.

below code will be used for split the transaction. (Select line -> Function -> Spilt)

public void split transactions(RefRecId _recId, Qty _qty)
{
            WMSOrderTrans     orderTrans;
             WMSOrderTrans    splitTrans;
             InventDim               inventDim;

          orderTrans                            = WMSOrderTrans::findOrderTrans(_recId,true);
          splitedTrans                          = orderTrans.split(_qty);
          inventDim                             =  orderTrans.inventDim();
          inventDim.inventBatchId     = shipmentDataTmp.InventBatchId;
          inventDim                             = InventDim::findOrCreate(inventDim);

           splitedTrans.selectForUpdate(true);
          splitedTrans.inventDimId    = inventDim.inventDimId;
           if(splitedTrans.validateWrite())
           {
                  splitedTrans.update();
           }
}

Note: findOrderTrans method will not be available in standard table. i have written extension class for WMSOrderTrans table.

[ExtensionOf(tableStr(WMSOrderTrans))]
final class WMSOrderTransExample_Extension
{

 static WMSOrderTrans findOrderTrans(RefRecId _recId, boolean _forupdate = false)
    {
        WMSOrderTrans   orderTrans;

        orderTrans.selectForUpdate(_forupdate);

        select firstonly orderTrans
            where orderTrans.RecId == _recId;

        return orderTrans;
    }
}

Monday, November 18, 2019

D365FO Receive transfer order with batch number registration using x++

The below code will used to Receive the transfer order with batch number registration through code

 public void processTransferOrder()
{
this.registerInventory("100001");
this.postTransferOrderReceive("10001",systemdateget());
}
public void postTransferOrderReceive(InventTransferOrderId _transferId, TransDate _postingDate)
{
        InventTransferTable         inventTransferTable;
        InventTransferParmTable     inventTransferParmTable;
        InventTransferUpdReceive    inventTransferUpdReceive;

        inventTransferTable = InventTransferTable::find(_transferId,true);
        if(inventTransferTable.TransferId)
        {
            inventTransferParmTable.initParmDefault();
            inventTransferParmTable.ParmId                  = RunBaseMultiParm::getSysParmId();
            inventTransferParmTable.TransferId              = inventTransferTable.TransferId;
            inventTransferParmTable.UpdateType              = InventTransferUpdateType::Receive;
            inventTransferParmTable.PrintTransferReceipt    = NoYes::No;
            inventTransferParmTable.ReceiveUpdateQty        = InventTransferReceiveUpdateQty::Registered;
            inventTransferParmTable.EditLines               = NoYes::Yes;
            inventTransferParmTable.ExplodeLines            = NoYes::Yes;
            inventTransferParmTable.TransDate               = _postingDate;

            inventTransferUpdReceive = InventTransferUpdReceive::newParmBuffer(inventTransferParmTable);
            inventTransferUpdReceive.run();
        }
}

public void postTransferOrderReceive(InventTransferOrderId _transferId)
{
        InventTransWMS_Register    inventTransWMS_register;
        TmpInventTransWMS           tmpInventTransWMS;
        InventTrans                            inventTranslocal;
        InventDim                              inventDimlocal;
        InventTransIdReceive            inventTransIdReceive;

 while select inventTransferLine
            where inventTransferLine.TransferId == _transferId
        {

            inventTranslocal        = InventTrans::findTransId(InventTransferLine.InventTransIdReceive, true);
            inventDimlocal          = inventTranslocal.inventDim();

            if(arrivalDataTmp.InventBatchId)
            {
                inventDimlocal.inventBatchId    = arrivalDataTmp.inventBatchId;
                inventDimlocal                  = InventDim::findOrCreate(inventDimlocal);
                inventTranslocal.inventDimId    = inventDimlocal.inventDimId;
            }

            inventTransWMS_register             = inventTransWMS_register::newStandard(tmpInventTransWMS);
            tmpInventTransWMS.clear();
            tmpInventTransWMS.initFromInventTrans(inventTranslocal);
            tmpInventTransWMS.ItemId            = inventTranslocal.ItemId;
            tmpInventTransWMS.InventQty         = arrivalDataTmp.qty;
            if(tmpInventTransWMS.validateWrite())
            {
                tmpInventTransWMS.insert();
                inventTransWMS_register.writeTmpInventTransWMS(tmpInventTransWMS
                                                           , inventTranslocal
                                                           , inventDimlocal);
     
                inventTransWMS_register.updateInvent(inventTranslocal);
            }
         
        }

Certificate issue in D365FO

We use virtual machines to perform development tasks which are provided as images by Microsoft run via Hyper-V on a local server. After the trail period expires the certificates will be expired. Here i have explained how to re activate the certificates

Error details

On your development machine you cannot open the application in the browser anymore and face an error message like
There is a problem with the server
Sorry, the server has encountered an error. It is either not available or it can't respond at this time. Please contact your system administrator.
If you check the event log using the Event Viewer you’ll find a warning message pointing to an ExpiredCertificateException there:
Process information: 
    Process ID: 14516 
    Process name: w3wp.exe 
    Account name: NT AUTHORITY\NETWORK SERVICE 
 
Exception information: 
    Exception type: ExpiredCertificateException 
    Exception message: Expired certificate for id 'C0E503DC8987D25B63897A7BE0B3E34BDCC89F41'.
   at Microsoft.Dynamics.AX.Configuration.CertificateHandler.LocalStoreCertificateHandler.GetCertificatesForId(String id)
etc.
Solution
Find Certificates
You can see the certificates that are relevant here using Manage computer certificates from Windows Start menu. Navigate to Certificates – Local Computer > Personal > Certificates.

In the column Expiration Date you can easily identify the ones that recently expired, in this case
  • DeploymentsOnebox.DaxRunnerTokenUserCertificate.pfx
  • DeploymentsOnebox.LcsClientCertificate.pfx
  • DeploymentsOnebox.MRClientCertificate.pfx
  • DeploymentsOnebox.SessionAuthenticationCertificate.pfx
Identify Thumbprint of Expired Certificate
Certificates get accessed by their thumbprint which is a 40-digit hexadecimal value. You can see it by double-clicking the certificate in the certificates viewer and open the Details tab.

Copy the thumprint values and make sure all letters are capital and remove all spaces.
example:43082FE50B4D02562C89EA728B2359C598E84886 
You can use any text editor or event VS, my preferred one for such operations is Notepad++. Make sure to run it as Administrator so you can save the files later without any issues. All three files we need are located in 

Clone the Certificate

Use PowerShell (and Run as Administrator, of course) to execute the following command (and make sure to replace the thumbprint with the one you just identified):
Set-Location -Path "cert:\LocalMachine\My"
$OldCert = (Get-ChildItem -Path 43082FE50B4D02562C89EA728B2363C598E84886)
New-SelfSignedCertificate -CloneCert $OldCert -NotAfter (Get-Date).AddMonths(999)
999 is the number of months the certificate will be valid until. Should be fine for quite some time.
The execution of this creates some output – copy and note the thumbprint of the newly created certificate. In the certificate manager you can see the clone (you might have to Refresh after a right click on the folder on the left).

Update References

The new thumprint values we need to update in web config, wif config and wif services config (take backup the files before modification). The files are available in the below path 

C:\AOSService\webroot:
  • web.config
  • wif.config
  • wif.services.config
Find the old thumprint 4(3082FE50B4D02562C89EA728B2359C598E84886) and replace new thumprint value which is generated.

Repeat this for all expired certificates. 

Reboot

Restart Batch, Management reporter, DMF, SQL and IIS services.
Restart the server