The Multi Report Action is designed to send email-based reports that list one or more user’s pending work in Onit (e.g., invoices that need to be approved, tasks that need to be completed).
This Action can be configured to solve two (very different) types of use cases:
- Example Use Case #1: The Employee Email: The ACME Corporation wants to send each Onit user a daily email that lists their pending work in Onit. That said, many of their users are responsible for approving Records in both the Contracts App and the Risks App. As a result, they don’t want to send each user two separate email reports each day (that would be pretty noisy). They instead want to send each user a single email each day that lists all of their pending work across both apps. A Multi Report Action can be configured to do exactly this! Below is an example screenshot of this report.
- Example Use Case #2: The Supervisor Email: Emily is the Legal Team’s supervisor. She wants to know which of her employees is the least busy on any given day. A Multi Report Action is created in Emily’s environment which sends her a summary of each of her reportees' pending Onit work. With this report Emily can more efficiently assign work to her team. Below is an example screenshot of this report.
The primary difference between the two use cases described above is who gets the email. In Example Use Case #1, the email tells each user what their pending work is. In Example Use Case #2, the email tells a supervisor what pending work currently exists in Onit for all of their reportees.
In this tutorial we’ll explain how to configure both of the solutions described above. Before jumping into configuration, however, we’ll provide a high-level overview of how the Multi Report Action operates.
The Multi Report Action is an especially advanced Action. Configuring this Action requires you to be familiar with many foundational Onit concepts, such as built-in Liquid objects (like current_user), Daily Schedules, raw filters (for users and Records), lists, Liquid loops, HTML, and CSS. If you are not an experienced Onit App Builder Developer you may find this Action difficult to master.
So how does this Action work?
The Multi Report Action has three primary configuration components to be aware of:
- The Daily Schedule: This component acts as the trigger - that is, the thing that tells the Multi Report Action when to run. In addition, this component identifies who should receive the email report. You must assign a Raw User Filter to the Daily Schedule that identifies each recipient of the report. Each recipient will see different data in the report.
If you’ve never used a Daily Schedule before, check out the relevant portion of this tutorial. You’ll need to understand how a Daily Schedule works in general in order to use the Multi Report Action.
The only event that can trigger a Multi Report Action is a Daily Schedule.
You’ll also need to understand how Raw User Filters work. Review the relevant portion of this tutorial if you’ve never worked with these types of filters before.
-
The Action: The Multi Report Action will fire once per user that was defined in the Raw User Filter that was assigned to the Daily Schedule (e.g., if this filter contains five users then the Multi Report Action will fire five separate times). When this Action runs it will compile all of the appropriate Records and send an email to the current user being processed. To compile the Records, the Action takes one of the two following approaches, depending on how it is configured:
- Approach #1: If you configure the Action to satisfy Example Use Case #1, the Action will consider the current_user to be the user that is being emailed. In other words, when configuring Multi Report, if you assign a Raw Transaction Filter to the Action (to identify which Records should appear in the report), and that filter references the current_user object (described below), then the Action will consider the current_user to be the user being emailed. You can configure the Action to send the user a list of pending work from either one or two Apps (two Apps is the limit).
If you’ve never worked with current_user before, be aware that this is a built-in object in Onit that grants you programmatic access to a user and their properties. Outside of the Multi Report Action, current_user typically refers to the user that is currently logged into Onit. Within the context of a Multi Report Action, however, this object refers to a user that the Action is currently processing. You can use Liquid to access current_user and Liquid can also be used when creating JSON filters. As a result, you can leverage current_user within a JSON filter to build a report that lists Records that have “pending work” for a specific user. Understanding current_user is important to using the Multi Report Action.
If you are unfamiliar with creating Raw Transaction Filters then check out this tutorial before continuing. This is another important concept that must be understood when working with the Multi Report Action.
-
Approach #2: If you configure the Action to satisfy Example Use Case #2, then the overall processing flow gets a bit more complicated. In this situation, the Multi Report Action still fires once per user that was defined in the Raw User Filter. That said, each time the Action fires it then rapidly changes who is considered to be the current_user. When the Action initially fires, it jumps into an uploaded list and searches this list for all users that report to the user being emailed (the user being emailed is considered to be the supervisor). Once the Action has built out this list of reportees, the Action then iterates over each reportee, one by one. As it does so, it swaps in the active reportee to be the current_user. As a result, any filters assigned to the Multi Report Action that reference current_user will be the active reportee being iterated over. This allows the Action to build out one email that lists all pending work for all users that report to the same supervisor.
- The List: This list is solely used to satisfy Example Use Case #2. As such, it identifies a reporting structure (i.e., who reports to whom). As will be explained later, this list must contain data within a specific column structure.
Configuring the Action
Now that you generally understand how the Multi Report Action works, let’s breakdown how it expects to be configured.
Below is a screenshot of the Action’s configuration properties. Under the screenshot we’ll explain each property, one-by-one.
In the breakdown below we’ll skip the top four configuration properties, as these are mostly basic and also not exclusive to the Multi Report Action.
-
Subject: Hard-coded text/HTML/CSS or Liquid expressions that define the email’s subject line.
- Body1: This configuration property is only relevant if you are working towards Example Use Case #1. In this situation, this property accepts hard-coded text/HTML/CSS or Liquid expressions that build out a list of Records. The Liquid expression that you use here has access to a special object named atoms, which contains a collection of Records that met the criteria defined in the Transaction Filter 1 property.
Example: Below is an example value that you might use for Body1. In this example, note that we are looping over the atoms object to build out an HTML table.
{% if atoms.size != 0 %}
<h2>Pending Invoices</h2>
<table cellpadding="4">
<tr>
<th align='left'>Invoice</th>
<th align='left'>Vendor</th>
<th align='left'>Matter Name</th>
<th align='left'>Date Received</th>
<th align='left'>Invoice Amount</th>
</tr>
{% for entry in atoms %}
<tr>
<td><a href="{{current_host}}/atoms/{{entry.id }}/">{{entry.name}}</a></td>
<td>{{entry.psb_vendor_name}}</td>
<td>{{entry.psb_matter_name}}</td>
<td>{{entry.received_date| date: "%m/%d/%Y"}}</td>
<td>{{entry.invoice_total|currency}}</td>
</tr>
{% endfor %}
</table>
{% endif %}
- Transaction Filter 1: This configuration property is only relevant if you are working towards Example Use Case #1. In this situation, the pre-created Raw Transaction Filter that you select here will be used to determine which Records are available to the atoms object that is accessible to any Liquid expression used in the Body1 property.
The filter that you assign to Transaction Filter 1 must exist within the App that contains the Records that you wish to list in Body1. This can be any App in your Onit environment.
If you are attempting to solve Example Use Case #2 instead of Example Use Case #1 then how you configure Body1 and Transaction Filter 1 does not matter (as these configuration properties won’t be used). Nonetheless, in this situation you must still select a Raw Transaction Filter from the Transaction Filter1 dropdown (even though it won’t be used) -- if you do not then the Multi Report Action will not work correctly.
Example: Below is an example filter that you might use for Transaction Filter1. In this example, note that we are only returning Records upon which the current_user (the user being emailed) has pending work.
[{"type":"user_pending_work","user_id":"{{current_user._id}}"}]
-
Body2: This configuration property is only relevant if: (a) you are working towards Example Use Case #1, and (b) you want your email-based report to include a different collection of Records than those outputted by the Body1 and Transaction Filter 1 configuration properties. This property accepts hard-coded text/HTML/CSS or a Liquid expression that builds out a list of pending work. The Liquid expression that you use here has access to a special object named atoms, which contains a collection of Records that met the criteria defined in the Transaction Filter 2 property.
-
Transaction Filter 2: This configuration property is only relevant if you are using Body2. In this situation, the pre-created Raw Transaction Filter that you select here will be used to determine which Records are available to the atoms object that will be available to any Liquid expression used in the Body2 property.
The filter that you assign to Transaction Filter2 must exist within the App that contains the Records that you wish list in Body2. This can be any App in your Onit environment.
If you are attempting to solve Example Use Case #2 instead of Example Use Case #1 then how you configure Body2 and Transaction Filter2 does not matter (as the configuration properties won’t be used). Similarly, if you are attempting to solve Example Use Case #1, but you only need to send one collection of Records (not two), then how you configure Body2 and Transaction Filter2 also does not matter. Nonetheless, in both of these situations, you must still select a Raw Transaction Filter from the Transaction Filter2 dropdown -- if you do not then the Multi Report Action will not work correctly.
- User List: This configuration property is only relevant if you are working towards Example Use Case #2. In this situation, this list defines the reporting hierarchy (i.e., who reports to whom). The list that you upload must match the format shown in the screenshot below.
If you’re not familiar with how to create and work with lists in Onit, refer to this tutorial. The list that you provide to a Multi Report Action must be created and uploaded like any other Onit list, but it must contain the exact column names shown above. The name that you use for your list does not matter.
You must assign a list even if you don’t need a reporting hierarchy or a supervisor email (e.g., if you are attempting to solve Example Use Case #1). In this situation, you can assign any list that has been uploaded to Onit, as this list will not actually be used by the Action. If you do not upload a list then the Multi Report Action will not work correctly.
It’s important to understand how the Multi Report Action knows whether or not you want to send a non-supervisor email (e.g., Example Use Case #1) or a supervisor email (e.g., Example Use Case #2). The Action makes this decision by searching the uploaded list for a column named user_email and a column named boss_email. If both columns exist in the list then the Action will send a supervisor email (e.g., Example Use Case #2), thereby ignoring the Body1, Body2, Transaction Filter1, and Transaction Filter2 configuration properties. If, on the other hand, the list does not contain both of these columns then the Action will send a non-supervisor email, thereby leveraging the Body1, Body2, Transaction Filter1, and Transaction Filter2 configuration properties.
Within the list, you must always list each supervisor as being their own reportee. For example, in the screenshot above, note that Michael Jordan is his own supervisor.
- User List Body: This configuration property is only relevant if you are working towards Example Use Case #2. In this situation, this property accepts hard-coded text/HTML/CSS or Liquid expressions that build out a list of Records for each reportee. The Liquid expression that you use here has access to a special object named atoms, which contains a collection of Records that met the criteria defined in the Filter For User List Body property.
The text/HTML/CSS/Liquid that you enter into User List Body will be generated once per reportee. For example, if the reporting hierarchy list identifies that the current user being emailed (the supervisor) has five reportees (as defined in the list), then the text/HTML/CSS/Liquid will be generated five separate times. Once all five iterations have run then all of their output will be combined together into a single email that is sent to the supervisor.
Example: Below is an example value that you might use for Body1. In this example, note that we are looping over the atoms object to build out an HTML table. Also note that the current_user object is being used to list the reportee’s name at the top of each respective table.
{% if atoms.size != 0 %}
{% assign new_atoms = atoms | sort: "phase" %}
<div style="width:100%px;">
<table cellpadding="4" style="font-size:14px;table-layout: fixed; width: 100%;margin-bottom:15px;">
{% for atom in new_atoms %}
{% if forloop.first %}
<tr>
<th colspan="3"><p style="text-align:left;color:red;font-size:13.5px;">{{current_user.name}}:</p></th>
</tr>
<tr>
<th style="background-color:#DDDDDD" align='left' width="110px;">Contract Name</th>
<th style="background-color:#DDDDDD" align='left' width="110px;">Contract Phase</th>
<th style="background-color:#DDDDDD" align='left' width="110px;">Request Date</th>
</tr>
{% endif %}
<tr>
<td style="word-wrap: break-word; vertical-align:top;"><a href="{{atom.current_host}}/apps/{{atom.app_id}}/atoms/{{atom.id}}">{{ atom.name }}</a></td>
<td style="word-wrap: break-word; vertical-align:top;">{{atom.phase}}</td>
<td style="word-wrap: break-word; vertical-align:top;">{{atom.contract_date | date:"%D"}}</td>
</tr>
{% endfor %}
</table>
</div>
{% endif %}
- Filter For User List Body: This configuration property is only relevant if you are working towards Example Use Case #2. In this situation, the pre-created Raw Transaction Filter that you select here will be used to determine which Records are available to the atoms object used in any Liquid expression defined in the User List Body property.
Any current_user object referenced in this filter refers to the current reportee being processed, not to the supervisor being emailed.
Example: Below is an example filter that you might use for Filter For User List Body. In this example, note that we are only returning Records upon which the current_user (the reportee) has pending work.
[{"type":"user_pending_work","user_id":"{{current_user._id}}"}]