As you know that sales order is the most important and useful feature of Dynamic-365 F&O and many developers have requirement to create sales order through x code. in this blog i will share the complete step by step process with x code. But before starting you should need to know the complete cycle of creating sales order.
1- Create Sales order
2- Confirm Sales order
3- Create Picking List
4- Register picking list
5- Create Picking Slip
6- Invoiced Sales order
So let’s get started,
first, you need to create the following three classes for Sys Operation.
SOSysOperationsController // SO Sales Order
SOSysOperationsContract
SOSysOperationsService
Then finally create Display Menu Item to open Parameter form.
step-1 : create a class with the following name and extends from SysOperationServiceController or copy paste the following code.
class SOSysOperationsController extends SysOperationServiceController
{
protected void new()
{
super(classStr(SOSysOperationsService), methodStr(SOSysOperationsService, process),
SysOperationExecutionMode::Synchronous);
}
public static SOSysOperationsController construct(SysOperationExecutionMode _executionMode =
SysOperationExecutionMode::Synchronous)
{
SOSysOperationsController controller = new SOSysOperationsController();
controller.parmExecutionMode(_executionMode);
return controller;
}
public static void main(Args _args)
{
SOSysOperationsController controller;
controller = SOSysOperationsController::construct();
controller.parmArgs(_args);
controller.startOperation();
}
}
step-2 : create another class for Contract to show pop-form and paste the following code
class SOSysOperationsContract
{
CustAccount custAccount;
ItemId itemId;
InventLocationId inventLocationId; //warehouse;
InventSiteId inventSiteId; //Site
[DataMemberAttribute("custAccount"), AifCollectionTypeAttribute("_custAccount", Types::String)] public CustAccount parmCustAccount(CustAccount _custAccount = custAccount)
{
custAccount = _custAccount; return custAccount;
}
[DataMemberAttribute("ItemId"), AifCollectionTypeAttribute("_ItemId", Types::String)]
public ItemId parmItemId(ItemId _itemId = itemId)
{
itemId = _itemId; return itemId;
}
[DataMemberAttribute("inventLocationId"), AifCollectionTypeAttribute("_inventLocationId", Types::String)]
public InventLocationId parmInventLocationId(InventLocationId _inventLocationId = inventLocationId)
{
inventLocationId = _inventLocationId; return inventLocationId;
}
[DataMemberAttribute("InventSiteId"), AifCollectionTypeAttribute("_InventSiteId", Types::String)]
public InventSiteId parmInventSiteId(InventSiteId _InventSiteId = InventSiteId)
{
InventSiteId = _InventSiteId;
return InventSiteId;
}
}
step-3 : create one more class for Services which is used for logic business copy and paste the following code.
class SOSysOperationsService extends SysOperationServiceBase
{
SalesTable salesTable;
SalesLine salesLine;
SalesFormLetter salesFormLetter;
SalesFormLetter_PickingList pickingList;
InventDim inventDim;
WMSPickingRouteID pickingRouteId;
NumberSeq numberSeq;
CustPackingSlipJour custPackingSlipJour;
SalesId salesId;
List list = new List(Types::String);
CustInvoiceJour custInvoiceJour;
InventTable inventTable;
CustTable custTable;
CustAccount custAccount;
str warehouse;
str site;
ItemId itemId;
public void process(SOSysOperationsContract _contract)
{
custAccount =_contract.parmCustAccount();
itemId = _contract.parmitemId();
site = _contract.parmInventSiteId();
warehouse = _contract.parmInventLocationId();
ttsbegin; salesTable.clear();
numberSeq = NumberSeq::newGetNum(SalesParameters::numRefSalesId());
numberSeq.used();
salesTable.SalesId = numberSeq.num();
salesId = salesTable.SalesId;
salesTable.initValue(); ///custAccount = "US-001";
if(CustTable::find(custAccount))
{
salesTable.CustAccount = custAccount;
}
else
{
info(strFmt("Customer account %1 doesn't exist.", custAccount));
}
salesTable.initFromCustTable();
if(InventLocation::find(warehouse).InventLocationId != "")
{
salesTable.InventSiteId = InventLocation::find(warehouse).InventSiteId;
salesTable.InventLocationId = inventlocation::find(warehouse).InventLocationId;
}
salesTable.insert();
inventTable.clear();
inventDim.clear();
salesLine.clear();
select * from inventTable where inventTable.itemId == itemId; //"1000";
salesLine.clear();
salesLine.SalesId = salesId;
salesLine.ItemId = inventTable.ItemId;
salesLine.itemIdChanged();
salesLine.initFromInventTable(InventTable::find(salesLine.ItemId));
if(Inventlocation::find(warehouse).InventLocationId != "")
{
inventdim.InventSiteId = InventLocation::find(warehouse).InventSiteId;
inventdim.InventLocationId = Inventlocation::find(warehouse).InventLocationId;
}
salesLine.InventDimId = InventdIm::findOrCreate(inventDim).inventDimId;
salesLine.createLine(NoYes::Yes, // Validate
NoYes::Yes, // initFromSalesTable
NoYes::No, // initFromInventTable
NoYes::Yes, // calcInventQty
NoYes::Yes, // searchMarkup
NoYes::Yes); //
salesLine.SalesPrice = 250;
salesLine.SalesQty = 3;
salesLine.LineDisc = 25;
salesLine.RemainSalesPhysical = salesLine.SalesQty;
salesLine.QtyOrdered = salesLine.calcQtyOrdered();
salesLine.RemainInventPhysical = salesLine.QtyOrdered;
salesLine.LineAmount= salesLine.calcLineAmount();
salesLine.update();
ttscommit;
info(strFmt("Sales order '%1' has been created", salesTable.SalesId));
////////CreateSOConfirmed ////////
ttsBegin; salesTable = SalesTable::find(salesId);
salesFormLetter = SalesFormLetter::construct(DocumentStatus::Confirmation);
salesFormLetter.update(salesTable, systemDateGet(), SalesUpdate::All, AccountOrder::None, false, false);
ttsCommit;
info(strFmt("Sales order '%1' has been Confirmed", salesTable.SalesId));
//////// CreateSOPickingList////////
ttsBegin; salesTable = SalesTable::find(salesId);
if (salesTable && salesTable.SalesStatus == SalesStatus::Backorder)
{
salesFormLetter = SalesFormLetter::construct(DocumentStatus::PackingSlip); salesFormLetter.update(salesTable,systemDateGet(),SalesUpdate::PackingSlip,AccountOrder::one, NoYes::No, NoYes::No, NoYes::No);
if (salesFormLetter.parmJournalRecord().TableId == tableNum(custPackingSlipJour))
{
custPackingSlipJour = salesFormLetter.parmJournalRecord();
info(strFmt('Packing Slip :%1 successfully created for Sales Order :%2', custPackingSlipJour.PackingSlipId, custPackingSlipJour.SalesId));
}
}
else
{
info(strFmt('%1 does not exsists in the system !', salesId));
}
ttscommit;
////////Picking List Register////////
ttsBegin;
select pickingRouteID from WMSPickingRoute where WMSPickingRoute.customer ==custAccount;
pickingRouteId = WMSPickingRoute.pickingRouteID;
if(pickingRouteId)
{
list.addEnd(WMSPickingRoute.pickingRouteID);
WMSPickingRoute::finishMulti(list.pack());
info(strFmt("Picking List Register with pickingRouteID: %1",pickingRouteId));
}
else
{
info(strFmt('%1 does not exsists in the system !', pickingRouteId));
}
ttsCommit;
/////// CreateSOPickingSlip ////////
ttsBegin; salesTable = salesTable::find(salesId); ///replace '001011' your sales order number
salesFormLetter = SalesFormLetter_PickingList::newPickingList();
salesFormLetter.transDate(systemDateGet());
salesFormLetter.update(salesTable, systemdateget(), SalesUpdate::All, AccountOrder::None, NoYes::No, NoYes::No);
ttsCommit;
info(strFmt("Sales order '%1' Picking List created", salesTable.SalesId));
////////Invoicing the sales order////////
ttsBegin;
try
{
salesTable = SalesTable::find(salesId);
if (salesTable && salesTable.SalesStatus == SalesStatus::Delivered)
{
salesFormLetter = SalesFormLetter::construct(DocumentStatus::Invoice);
salesFormLetter.update(salesTable, systemDateGet(), SalesUpdate::All, AccountOrder::None, NoYes::No, NoYes::No, NoYes::No, NoYes::Yes);
if (salesFormLetter.parmJournalRecord().TableId == tableNum(CustInvoiceJour))
{
custInvoiceJour = salesFormLetter.parmJournalRecord();
info(strFmt('Invoice for Sales Order: %1 has been posted, Invoice: %2', custInvoiceJour.SalesId, custInvoiceJour.InvoiceId));
}
}
else
{
info(strFmt('%1 does not exsists or not in delivered state.', salesId));
}
}
catch (Exception::Error)
{
info(strFmt('Failed to post Sales order invoice for %1.', salesId));
}
ttscommit;
}
}