Dynamics 365 Project Service Automation: Custom Product Bundles

Tested on:
Dynamics 365 CE with PSA version 3.8

Enabling Product Bundles has long been a discussion when it comes to Dynamics 365 for Project Service Automation. The OOTB Product Bundles are not supported in PSA but the ask for the feature is out there. It’s something I’ve been running into in pretty much every single presales case that involves materials in some shape or form. As support for the OOTB bundles isn’t on the short term radar, a bit of creativity is needed to build something that gets us closer to bundles in PSA.

An upside to customizing and building a bundle feature is integrations and enabling bundle-like features in PSA. The OOTB bundles can cause an integration between an ERP and D365 CE to be hellish. Personally I prefer a simple Product Catalogue structure without the burden of the OOTB Product Bundles and Product Families. This blog post covers one approach to enabling bundles, while keeping things simple. Feel free to take the ideas on this post further, if you have a more complex need or if you want to tweak the covered approach.

The structure of Custom Product Bundles

Those of you who are familiar with PSA know that Opportunities, Quotes, Orders and Invoices have a specific layered structure: There is a record (like an Order), the record has lines and the lines have details (excluding Opportunities). This same approach applies to this blog post’s example but the layering is kept simple by using Product Bundles and Product Bundle Details. If your requirement calls for something more complex, take the idea and develop it further.

What’s important to consider is the part of your process where a logic for bundles is needed. This example focuses on creating bundles on an Order, however your processes may require bundles on an Opportunity or a Quote. You can make the appropriate changes to the necessary entities, fields and the Flow yourself.

Product Bundle entity

This entity is holds the basic information about the bundle. There is a lookup to the Product entity as well as to the Order entity: Each Product Bundle is related to a Product in the Product Catalogue and to a single Order. This makes it possible to build Order specific bundles. In this example, when a Product Catalogue’s product named ISV Product License is added on the Order named Product Bundle Demo Order, all Product Bundle Details are added as well. This is done with Flow, when the conditions it contains are fulfilled.

Product Bundle entity.

Product Bundle Detail entity

The Product Bundle Detail entity contains a “recipe” for a Product Bundle. A Product set in the Product lookup is added on an Order when a Product Bundle entity’s Parent Product is added by creating an Product-based Order Line. Quantity is defined on Product Bundle Detail to control the amount of Products that are to be added on an Order Line. Integration scenarios may require additional validation logic for quantity values. In this example, when a Product named ISV Product License is added on Order Product Bundle Demo Order, the Products named Dynamics 365 CE Project Service Automation License and Office 365 E5 License from the two Product Bundle Details are both added on the same Order as well.

Product Bundle Detail entity.

The Flow

This example focuses on Product Bundles on Orders. The Flow fires off when a Product-based line on an Order is created. Trigger Conditions are a new feature as I’m writing this blog post and they can be used to control when a Flow fires off. As this Flow creates multiple Order Lines, a Trigger Condition is used to define that the Flow only fires off when a bundle is in question. For this example, the value in a two option field has to be true for the trigger to fire off.

When a Product-based Order Line is created and the Product Bundle two option field is set to Yes, the Flow fires off.
Step 1. Trigger Conditions can be used to control when a Flow fires off. The expression in this Flow is:
@equals(triggerOutputs()?[‘body/d2d_productbundle’], true)

The next step in the Flow is a condition that checks that the Select Product field has a value of Existing. The other option would be write-in. Referencing two options fields can be a bit tricky so be sure to watch MVP Elaiza Benitez‘s WTF Episode 6, where she covers two option fields in Flow. If the Order Line being created is for a write-in product, the Flow is terminated as cancelled. If an existing product is in question, we’ll move to the scope on the true side.

Step 2. A condition is used to check that an existing product is being used.

The first step in the scope is to list all Product Bundle records. The Filter Query narrows down the results so that we can get our hands on those Product Bundle records that match both the Order in question and the Product that is being added on an Order Line. Change the Filter Query according to your needs, if you’re creating bundles on Opportunities or Quotes.

Step 3. Product Bundle records related to the Order and Product in question are listed.

The fourth step consists of an apply to each, based on the previous action where Product Bundles were listed. A get records action will give us access to the Product Bundle records from the previous list records action. We can then list all related Product Bundle Details. A Filter Query will again narrow down the results.

Step 4. Getting bundle records and listing Project Bundle Details.

The fifth and final step is again an apply to each, where a get records action is used to get us access to Product Bundle Details. Then another get records action will get us the Unit from the initial Get Product Bundle Detail records from previous list action. The final action in the Flow is to create a new Product-based Order Line. After this, an Order will have Product-based Order Lines based on what is defined under the original Product Bundle and Product Bundle Detail records.

A math function is used for quantity, when new Order Lines are created based on Product Bundle Details. With math functions, the quantity on Product Bundle Details can be multiplied with the quantity on the Order Line that is being created. The expression for the math functions in this example is:
mul(triggerOutputs()?[‘body/quantity’], outputs(‘Get_Product_Bundle_Detail_records_from_previous_list’)?[‘body/d2d_quantity’])

Step 5. Second apply to each.
The Product Bundle Flow.

As always, the TDG Power Platform Bank contains both a managed and an unmanaged solution with the Flow, the Product Bundle entity and the Product Bundle Detail entity. The two option field Product Bundle d2d_productbundle for the Order Line entity is also included, however I have not included a Model-driven app or the main form for the Order Line entity in the solutions.

All my blog posts reflect my personal opinions and findings unless otherwise stated.