Header Ads Widget

How to Create a Sales Quotation Programmatically in D365FO Using X++ (Updated Code Example)

 

📘 Introduction

In this blog post, we’ll walk through how to create a Sales Quotation via X++ in Dynamics 365 Finance and Operations. This is especially helpful when automating quotation generation based on business rules or third-party integrations.


🧾 Business Scenario

Imagine a use case where sales quotations need to be created in bulk from an external system, or as part of a scheduled batch job. Instead of manually creating them, we can use X++ to generate and confirm the quotation programmatically.


✅ Steps Covered in Code

  1. Create Sales Quotation header using AxSalesQuotationTable.

  2. Insert multiple quotation lines using AxSalesQuotationLine.

  3. Set inventory dimensions.

  4. Send and confirm the quotation using SalesQuotationEditLinesForm.


🧠 Updated X++ Code in D365FO

x++
public static void CreateSalesQuotationD365FOJob(Args _args) { AxSalesQuotationTable quotationHeader; AxSalesQuotationLine quotationLine; SalesQuotationEditLinesForm quotationForm; SalesQuotationUpdate quotationUpdate; SalesQuotationTable quotationTableRec; SalesQuotationLine quotationLineRec; InventDim dimension; container itemList = ["Item001", "Item002", "Item003"]; int itemIndex; SalesQty lineQty = 5; SalesPrice linePrice = 200; SalesLineDisc lineDisc = 5; SalesLinePercent lineDiscPercent = 2; ParmId formParmId; // Create Sales Quotation Header quotationHeader = new AxSalesQuotationTable(); quotationHeader.parmCustAccount("CUS-001"); quotationHeader.parmCurrencyCode("USD"); quotationHeader.save(); // Insert Quotation Lines for (itemIndex = 1; itemIndex <= conLen(itemList); itemIndex++) { quotationLine = new AxSalesQuotationLine(); quotationLine.axSalesQuotationTable(quotationHeader); quotationLine.parmQuotationId(quotationHeader.parmQuotationId()); quotationLine.parmItemId(conPeek(itemList, itemIndex)); quotationLine.parmSalesQty(lineQty); quotationLine.parmSalesPrice(linePrice); quotationLine.parmLineDisc(lineDisc); quotationLine.parmLinePercent(lineDiscPercent); quotationLine.parmCurrencyCode("USD"); dimension.InventSiteId = "SITE001"; dimension.InventLocationId = "LOC01"; quotationLine.parmInventDimId(InventDim::findOrCreate(dimension).InventDimId); quotationLine.save(); } // Send Quotation if (!quotationHeader.salesQuotationTable()) return; quotationForm = SalesQuotationEditLinesForm::construct(DocumentStatus::Quotation); formParmId = quotationForm.parmId(); quotationForm.initParmSalesQuotationTable(quotationHeader.salesQuotationTable()); quotationForm.parmId(formParmId); quotationForm.parmTransDate(systemDateGet()); quotationForm.prePromptInit(); quotationForm.initParameters( NoYes::No, NoYes::No, NoYes::No, NoYes::No, NoYes::No, "InitialQuote", NoYes::No); quotationForm.run(); // Confirm Quotation quotationForm = SalesQuotationEditLinesForm::construct(DocumentStatus::Confirmation); formParmId = quotationForm.parmId(); quotationForm.initParmSalesQuotationTable(quotationHeader.salesQuotationTable()); quotationForm.parmId(formParmId); quotationForm.parmTransDate(systemDateGet()); quotationForm.prePromptInit(); quotationForm.initParameters( NoYes::No, NoYes::No, NoYes::No, NoYes::No, NoYes::No, "FinalApproval", NoYes::No); quotationForm.run(); }

📌 Key Notes

  • AxSalesQuotationTable and AxSalesQuotationLine are standard wrapper classes that make table interaction cleaner and more maintainable.

  • Always use InventDim::findOrCreate() to avoid hardcoding dimension IDs.

  • The quotation lifecycle is handled by the SalesQuotationEditLinesForm.


🧩 Additional Tips

  • To integrate this with external systems, consider creating a custom service class or logic in a Data Entity.

  • Use FormLetter classes if you need more flexibility for confirming or updating sales quotations.


✅ Conclusion

Creating sales quotations in D365FO through X++ is a powerful technique that allows automation and efficiency in sales processes. This blog post showed a complete, modernized example using updated patterns and naming conventions.

If you found this post useful, follow our blog for more real-world D365FO development examples!

Post a Comment

0 Comments