Introduction
In Microsoft Dynamics 365 Finance & Operations (D365 F&O), Purchase Orders can be confirmed programmatically using the standard PurchFormLetter framework.
This approach is useful for:
- Custom business processes
- Batch jobs
- Integrations
- Custom forms
- Automated procurement processes
Using the standard framework is recommended instead of directly updating the Purchase Order status because the framework executes the required standard business logic and validations.
Requirement
The requirement is to provide a reusable X++ method that can receive a PurchTable record and confirm the Purchase Order programmatically.
The solution should:
- Validate the Purchase Order.
- Create the required parameter records.
- Execute the standard Purchase Order confirmation framework.
- Handle the operation within a transaction.
- Return the Purchase Order after successful processing.
Solution
The standard PurchFormLetter framework can be used for Purchase Order confirmation.
The high-level flow is:
PurchTable
|
v
Create PurchFormletterParmData
|
v
Create PurchParmUpdate
|
v
Populate PurchParmTable
|
v
Create PurchFormLetter
|
v
Run Confirmation
|
v
Purchase Order Confirmed
X++ Implementation
public static PurchTable confirmPurchaseOrder(PurchTable _purchTable)
{
PurchFormLetter purchFormLetter;
PurchFormletterParmData purchFormLetterParmData;
PurchParmUpdate purchParmUpdate;
PurchParmTable purchParmTable;
ttsBegin;
// Create parameter data
purchFormLetterParmData = PurchFormletterParmData::newData(
DocumentStatus::PurchaseOrder,
VersioningUpdateType::Initial);
purchFormLetterParmData.parmOnlyCreateParmUpdate(true);
purchFormLetterParmData.createData(false);
purchParmUpdate = purchFormLetterParmData.parmParmUpdate();
// Populate parameter table
purchParmTable.clear();
purchParmTable.TransDate =
DateTimeUtil::getSystemDate(
DateTimeUtil::getUserPreferredTimeZone());
purchParmTable.DocumentDate =
DateTimeUtil::getSystemDate(
DateTimeUtil::getUserPreferredTimeZone());
purchParmTable.Ordering = DocumentStatus::PurchaseOrder;
purchParmTable.ParmJobStatus = ParmJobStatus::Waiting;
purchParmTable.PurchId = _purchTable.PurchId;
purchParmTable.PurchName = _purchTable.PurchName;
purchParmTable.DeliveryName = _purchTable.DeliveryName;
purchParmTable.DeliveryPostalAddress =
_purchTable.DeliveryPostalAddress;
purchParmTable.OrderAccount = _purchTable.OrderAccount;
purchParmTable.CurrencyCode = _purchTable.CurrencyCode;
purchParmTable.InvoiceAccount = _purchTable.InvoiceAccount;
purchParmTable.ParmId = purchParmUpdate.ParmId;
purchParmTable.insert();
// Create Purchase Order confirmation
purchFormLetter = PurchFormLetter::construct(
DocumentStatus::PurchaseOrder);
purchFormLetter.transDate(
DateTimeUtil::getSystemDate(
DateTimeUtil::getUserPreferredTimeZone()));
purchFormLetter.proforma(false);
purchFormLetter.specQty(PurchUpdate::All);
purchFormLetter.purchTable(_purchTable);
purchFormLetter.parmParmTableNum(purchParmTable.ParmId);
purchFormLetter.parmId(purchParmTable.ParmId);
purchFormLetter.purchParmUpdate(
purchFormLetterParmData.parmParmUpdate());
// Execute confirmation
purchFormLetter.run();
ttsCommit;
return _purchTable;
}
How It Works
Create Parameter Data
PurchFormletterParmData prepares the parameter framework required for Purchase Order confirmation.
PurchFormletterParmData::newData(
DocumentStatus::PurchaseOrder,
VersioningUpdateType::Initial);Create PurchParmTable
The Purchase Order information is copied into PurchParmTable, which associates the confirmation request with the Purchase Order.
Create PurchFormLetter
PurchFormLetter::construct() creates the standard framework responsible for processing the confirmation.
Execute Confirmation
Finally:
purchFormLetter.run();executes the standard Purchase Order confirmation process.
Important Considerations
Before calling the method, the implementation should validate:
- Purchase Order exists.
- Purchase Order is in a valid status for confirmation.
- Purchase Order belongs to the correct legal entity.
- Required vendor and Purchase Order information is available.
- The Purchase Order is not already being processed by another process.
For batch and integration scenarios, logging and retry handling should also be considered.
Risks
| Risk | Mitigation |
|---|---|
| PO already confirmed | Validate status before processing |
| Duplicate integration request | Implement idempotency |
| Concurrent confirmation | Use appropriate locking/status validation |
| Confirmation failure | Allow standard framework errors to propagate/log them |
| Transaction rollback | Clearly define transaction ownership |
| Custom/ISV extensions | Perform regression testing |
Recommended Approach
The recommended design is to keep the confirmation logic in a reusable service/class and allow different processes to call it:
+----------------+
| Batch / API / |
| Custom Form |
+-------+--------+
|
v
+-----------------------+
| PO Confirmation |
| Service |
+-----------+-----------+
|
v
+-----------------------+
| PurchFormLetter |
| Standard Framework |
+-----------+-----------+
|
v
+-----------------------+
| Confirm Purchase |
| Order |
+-----------------------+This avoids duplicating confirmation logic across different customizations.
Conclusion
PurchFormLetter is the preferred standard framework for programmatically confirming Purchase Orders in D365 F&O.
The key recommendation is to use the standard framework rather than directly updating Purchase Order status fields. This helps ensure that standard business logic and validations are executed correctly.
For production implementations, additional consideration should be given to validation, transactions, concurrency, idempotency, logging, and integration retry handling.

Like
Report
*This post is locked for comments