Workflows in Dynamics 365 Finance and Operations (F&O) are used to add approval steps to a business process flow. For example, in the purchase-to-pay flow, if approval is enabled and the workflow is configured, assigning a new or different bank account to a vendor account is subject to approval. This means that in F&O, an approver must approve the new bank account before payments can be made to the vendor's new bank account. I highly recommend this vendor bank approval workflow to all my clients, by the way. So, if a user manually assigns a new or different bank account to a vendor in F&O, the system requires approval by an approver.
In the field, one of my clients has an integration (built by a partner, not me) from an external application where the vendors and bank accounts are created and then sent to F&O. The vendors and vendor bank accounts are imported into F&O using a custom framework. The custom framework in F&O creates the vendors and vendor bank accounts and assigns the vendor bank account to the vendor. After this assignment of the bank account to the vendor account, the vendor account is blocked for payment, and the system requires approval via workflow.
Because of the sheer volume that is processed, it's not feasible for someone to manually submit all those new vendors and vendor bank accounts to the workflow. It's not possible to submit them in bulk; they have to be submitted individually.
So, how do we solve this without increased risk? By creating a small customization using X++ and the SysOperation framework. The customization uses the X++ Workflow::activateFromWorkflowType method and automatically submits the vendor and vendor bank accounts to the approval workflow.
Note: The records are only submitted to the workflow, not approved. The approval part of the flow remains and has to be done by an authorized workflow approver in F&O. In this instance, we can identify the source of the data as "external," so we filter the data so that only the externally created data is handled by this customization. The approval is still manual not automatic due to the risk.
To conclude, data can be submitted to the workflow using the Workflow::activateFromWorkflowType method and approvals should not be automated because of the risk.
Code
The code below is the core of my solution. With the SysOperation framework, I always use a minimum of 3 classes. The code below extends the vendor and vend bank account tables and the methods are in the business logic class. If you need more detailed information about what I mean with "3 classes", have a look at Multi selection on forms with the SysOperation framework.
VendTable extension method
public boolean avCanSubmitToWorkflow() { boolean ret = true; if (!this.RecId) { ret = false; } if (this.WorkflowState == VendTableChangeProposalWorkflowState::NotSubmitted) { VendTableChangeProposal changeProposal; changeProposal.disableCache(true); select firstOnly RecId from changeProposal where changeProposal.VendTable == this.RecId; if (!changeProposal) { ret = false; } } else { ret = false; } return ret; }
VendBankAccount extension method
public boolean avCanSubmitToWorkflow() { boolean ret = false; if (this.WorkflowState == VendBankAccountChangeProposalWorkflowState::NotSubmitted && this.requiresApproval()) { ret = true; } return ret; }
Vendor code
private Counter runVendors() { Counter cntr; QueryRun queryRun; VendTable vendTable, vendTableUpdate; queryRun = new QueryRun(this.buildQueryVendTable()); while (queryRun.next()) { vendTable = queryRun.get(tableNum(VendTable)); if (vendTable.avCanSubmitToWorkflow()) { if (Workflow::activateFromWorkflowType(workFlowTypeStr(VendTableChangeProposalWorkflow), vendTable.RecId, "Auto submit approve manually", NoYes::No)) { update_recordset vendTableUpdate setting WorkflowState = VendTableChangeProposalWorkflowState::Submitted where vendTableUpdate.RecId == vendTable.RecId && vendTableUpdate.WorkflowState == VendTableChangeProposalWorkflowState::NotSubmitted; cntr++; } } } return cntr; }
Vendor bank account code
private Counter runVendBankAccounts() { Counter cntr; QueryRun queryRun; VendBankAccount vendBankAccount, vendBankAccountUpdate; queryRun = new QueryRun(this.buildQueryVendBankAcc()); while (queryRun.next()) { vendBankAccount = queryRun.get(tableNum(VendBankAccount)); if (vendBankAccount.avCanSubmitToWorkflow()) { if (Workflow::activateFromWorkflowType(workFlowTypeStr(VendBankAccountChangeProposalTemplate), vendBankAccount.RecId, "Auto submit approve manually", NoYes::No)) { update_recordset vendBankAccountUpdate setting WorkflowState = VendBankAccountChangeProposalWorkflowState::Submitted where vendBankAccountUpdate.RecId == vendBankAccount.RecId && vendBankAccountUpdate.WorkflowState == VendBankAccountChangeProposalWorkflowState::NotSubmitted; cntr++; } } } return cntr; }
No comments:
Post a Comment