How to Include Chatter Posts on Printable Views

In Spring ’19 Salesforce added a Printable View button to many Standard and all Custom Objects. The Printable View includes the current user’s page layout and related lists. What it does not include are any Posts that users have added to the Chatter Feed.

In this Blog Post, I’ll show you how to add a button to your Lightning Record Page that will create your Printable View including Chatter Posts.

This post covers how to add this capability to the Case object. You can apply all of these same steps to support this feature in other objects of your choice.

The first step is to add a custom field to Case to store the details from the Chatter Posts.

Here I create a Rich Text field called Case Chatter Feed with a default length of 32,768 characters.

The next step is to update my Page Layout to include this new field. In this example, I have also added a new Section to put it in.

Now, when I want to create a new Printable View of my record, I must first populate this field with the current content of the record’s Chatter Feed. I can handle this step by running a Flow. The only attribute I need to pass into this Flow is the record ID (varCaseId) from the Case page.

In this body of this Flow I get all of the user created Chatter Posts and Comments and insert them into my new custom field. Later in this post I will show you how the first and last steps in this Flow tie everything together.

In the first Get Records step, I get all Feed Item records related to the Case and specify the fields I will be using later in the Flow.


I then Loop through each of those Feed Items by referencing the collection variable {!Get_Feed_Item_Records} that was automatically created by the Get Records step. Here I also create a new single Record variable {!loopFeedItem} to store each record inside of the loop.


In the Decision step, I skip any Feed Items records with a blank body. This way I’m only processing user created posts. You can modify this logic if you want to include more details.


Because the Feed Item records only include the user’s ID, I use a Get Records step to get the user’s name to include in my Chatter Feed field.


In the Assignment step, I build out each of the Feed Item fields I want to show as part of the Post as well as the formatting of the information. This is done by using the Add operator to build out the full feed one piece at a time.


We end up turning this from the Chatter Feed:

Into this in our custom field:

I’ve used a combination of Formula resources and Text Template resources to build out each post.

  • ttLineSeparator (Text Template)
    • ~
      ~
  • fSpaceSeparator (Text Formula)
    • ” | “

For each Feed Item I do another Get Records to find any Feed Comments associated with the current Feed Item.


I then Loop through each of those Feed Comments by referencing the collection variable {!Get_Feed_Comment_Records} that was automatically created by the previous Get Records step. Here I also create a new single Record variable {!loopFeedComment} to store each record inside of the loop.


Just like I did with Feed Items, I skip any Feed Comments with a blank body in the Decision step.


Because the Feed Comment records only include the user’s ID, I use a Get Records step to get the user’s name to include in my Chatter Feed field.


I continue to build out my chatter posts field by adding Feed Comments and formatting in the Assignment step.

Here I’ve used some additional formatting variables.

  • ttCommentSeperator (Text Template)
    • .
  • fCommentIndent (Text Formula)
    • ” —– “

After the Feed Comment Loop is completed, I add one more Assignment step to add a separator before the next Feed Item.

  • ttItemSeparator (Text Template)
    • ______________________________________________________________________________________________

After the outer Feed Item Loop is finally completed, I add one more Assignment step to cleanup the separations between the lines.

This is a little trick you can use to put line separators (Carriage Return, Line Feed) inside of a text field. Text Templates support separate lines but they can’t contain just a single separator so my ttLineSeparator Text Template includes a single special character on each line that get removed by my fFixCRLF Formula.

  • ttLineSeparator (Text Template)
    • ~
      ~
  • fFixCRLF (Text Formula)
    • SUBSTITUTE({!varBodyText},”~”,””)

The last piece needed to get my Chatter Posts into the Case record so they can be displayed is to add an Update Records step to save the record with the newly built Chatter Feed details.


Now I will show you how to add a couple more pieces to this Flow so we can use it on a Lightning Record Page and have it open a new Tab with the Printable View.

I want to put this Flow on the Case Lightning Record Page but I don’t want it to run until selected by the user. I handle this by adding a Screen to the beginning of the Flow that displays a custom image and waits until the user clicks on it before continuing with the rest of the Flow. Here I’m using the Image Button Flow Screen Component from unofficialsf.com. I’ve added the display, hover and pressed images for the button as Static Resources to my org so they can be referenced by the Image Button component. (Follow the Image Button link for complete documentation on this component)

Select the display image and the action to take when clicked
Select the images to display during mouse hover and mouse press
Select the size of the image

The final step in my Flow is an Action that opens a new Tab with the URL for the Printable View of the Case record. Here I’m using another component from unofficialsf.com. This one is the Open URL Flow Action. (Follow the Open URL link for complete documentation on this component)

The URL for the Printable View is created with a formula that combines the Base URL for the running org with the Case Record Id and the Printable View URL identifier.

  • fPrintableViewURL (Text Formula)
    • LEFT({!$Api.Partner_Server_URL_340}, FIND(‘/services’, {!$Api.Partner_Server_URL_340})) & {!varCaseId} & “/p”

All I need to do now is to activate then add this Flow to the Case Lightning Record Page and configure it to pass in the recordId to my varCaseId input variable.


I’ve created a couple of Installation Packages for those who want to try this out in their orgs. The first package includes both the Image Button and Open URL components. That package will only install in your org if you have neither of those components already installed. The second package does not include those components and the provided Flow must be edited to access them. I’ve provided instructions in the Flow showing how to do that.

1 – Install Package with Custom Case Field, Components, Image Static Resources and Sample Flow

Production
Sandbox

Post Package Install Instructions

  • Add the new field ( Case Chatter Feed ) to the Case Page Layout
  • Activate the Flow ( Case – Printable View with Chatter Posts )
  • Add the Flow to the Case Lightning Record Page

2 – Install Package with Custom Case Field, Image Static Resources and Sample Flow (No Components)

Production
Sandbox

Post Package Install Instructions

  • Make sure you have the current version installed for the Image Button component
  • Make sure you have the current version installed for the Open URL component
  • Add the new field ( Case Chatter Feed ) to the Case Page Layout
  • Edit the Flow ( Case – Printable View with Chatter Posts – Template )
    • See the instructions in the 2 Flow Screen elements
  • Activate the Flow
  • Add the Flow to the Case Lightning Record Page



Enhance your Flows with Data Tables – Part 4

Lightning Flow Screen Components are a great way to add power to and improve the look of your Flows.  In this four-part series, I’ll show you how a single component, designed to display a list or table, can enhance your Flows.

The unofficialsf.com website includes datatableFSC as one of many Flow Screen Components you can use for your own organization.

This component is derived from the datatable Lightning Base Component.  I recently enhanced the base component to be more powerful and Admin friendly.

I will be showing you some examples about how the Datatable component:

  • Can be used in place of a Record Choice Set (Dynamic Record Choice) for selecting a record while displaying more than a single field
  • Can be used to select multiple records from an object then perform additional actions on them
  • Can be used to present your data in an interactive and visually enhanced format
  • Can be used to display and inline edit a collection of records

Part 4: Inline Editing

In November of 2019, Kirill Boyarkin, updated this component to support inline editing. Now you can specify individual columns as editable and the changed values will be passed back to your Flow so you can update the object records.

In this example, I created a simple Flow to present a list of Account records with the Annual Revenue column set to editable.

The Get Records node populates an SObject Collection variable with the records and fields I want to display in my datatable.

I pass that collection into the datatableFSC component on my Flow Screen, define the columns I want to display and set the Revenue column to be editable.

This component has a lot of attributes, so make sure you assign any output attributes in the Output Values section. For my output attributes, I select “Manually assign variables (advanced)” and assign my new SObject collection variable to the same Collection – Accounts attribute I used to pass in my records.


NOTE: The output attribute for selected records (Output Selected Accounts) is only used for identifying selected records in the datatable and may not contain the updated values. When you are combining editing and selecting in a single table, use the “Output Selected Accounts” attribute to get the list of selected records and their IDs and use the “Collection – Accounts” attribute to access the edited records.


The final Update Records node in my Flow is to update the Account object with my collection of edited records.


If you Show the checkbox column with your datatable, you will be able to select multiple rows and optionally apply an edit to all of the selected rows.

By default, the component will display Cancel and Save buttons once you start making edits. You can bypass this option by setting the “Show Save and Cancel Buttons?” attribute to False. Any edits will then be automatically saved once you hit the Next button.

Note: If there are a large number of records in the datatable, the Next button will always display at the bottom of the screen, but the user will have to scroll to the end of the table to see the Cancel and Save buttons.


See the complete series on how you can use the datatable Flow Screen Component in your Lightning Flows.

Part 1 – Use a Datatable to present a dynamic choice for record selection in a Flow.
Part 2 – Use a Datatable in a Flow to select and act on a collection of records.
Part 3 – Use a Datatable to display a formatted, interactive table in your Flow.
Part 4 – Use a Datatable to inline edit a group of records.



How to make a field POP on the page

Sometimes you just want to make a certain field stand out on your page layout.

The standard detail section for a Record Page shows all fields the same way.

Quick – Where’s the Case Number?

I wanted to be able to see, at a quick glance, what the Case # was on my internal Software Development Case record page. How could I make it stand out better while keeping it a part of the standard detail section?

Is this better?

WOW, how did I make that happen?

With a new custom field and a simple update to my Case Process Builder I’m able to display a formatted version of the Case #.

Start with a new Rich Text Area field. I gave mine the same “Case Number” label as the standard field (be sure to make the field API name unique) and the minimum values of 256 characters and 10 lines visible.

To update the value in the new Case Number field, I added a node to my Case Process Builder. (Best practice is to always have a single Process Builder per object)

Any time a new Case record is created, I update the Rich Text field to contain the Case Number along with the HTML tags to display it with a size 22pt bold font. (You can modify these tags to format the field any way you wish)

“<p><b style=\”font-size: 22px;\”>” & [Case].CaseNumber & “</b></p>”

Criteria Node

Immediate Action

The final step is to replace the standard Case Number field on your Page Layout with the new Rich Text Case Number field.


Since this new feature only updates the field for new Case records, I created a simple Flow to run once to set the field value for all existing records.


The Flow is designed to update a single record based on the $Record Id that gets passed to it. By configuring the Start node as a 1-time scheduled Flow, it can select and pass all existing records one at a time through the Flow. (I have multiple Case Record Types in my org so I’m only selecting the one I want to use this feature on)


The Get Records node reads the Case record with the $Record.Id passed in by the scheduler.


I created a Variable with the formatting I wanted for my Rich Text value.


I also created a Formula to insert the record’s Case Number value into my HTML styling.


The Assignment node updates the custom Rich Text field in the Case record.


Finally, the Update Records node saves the changed record.


Now, just save and activate your Flow and wait until the scheduled time for it to do its updating.

If you want to get even more creative in how you implement this feature, you could even update your Process Builder to fire on certain field changes and do things like:

  • Set the color based on whether or not the Case is open or closed
  • Add a unique icon if the Contact is an internal user vs an external customer
  • Change the font size based on the Case priority
  • … you get the idea, be creative


Enhance your Flows with Data Tables – Part 3

Lightning Flow Screen Components are a great way to add power to and improve the look of your Flows.  In this four -part series, I’ll show you how a single component, designed to display a list or table, can enhance your Flows.

The unofficialsf.com website includes datatableFSC as one of many Flow Screen Components you can use for your own organization.

This component is derived from the datatable Lightning Base Component.  I recently enhanced the base component to be more powerful and Admin friendly.

I will be showing you some examples about how the Datatable component:

  • Can be used in place of a Record Choice Set (Dynamic Record Choice) for selecting a record while displaying more than a single field
  • Can be used to select multiple records from an object then perform additional actions on them
  • Can be used to present your data in an interactive and visually enhanced format
  • Can be used to display and inline edit a collection of records

Part 3: Displaying Tables in a Flow

How often have you had to use special formula fields or resort to building text output one line at a time in your flows to present a list of records?

In this post I’ll show you how to use the Lightning Datatable Flow Screen Component to turn this:

Into this:

I have an application where I wanted to use a Flow to display data from three separate objects on a single screen. In the original version of this Flow I created a text variable for each table and used a text template variable to insert each line into the table. Formatting was a challenge and alignment was impossible with this method.

Each table is created with a separate text variable.

The tables are built by appending a text template to the variable for each record.

The Text Template variable is used to append each of the tables fields together.

The final result includes all of the information, but it is not very easy to read.


In the new version of this Flow, I use 3 different Lightning Flow Screen Components (datatableFSC, horizontalRuleFSC, & navigationButtonFSC) from the unofficial.com website.

Other than a few Display Text sections, this screen is composed entirely of Lightning Flow Screen Components.

The horizontalRuleFSC components are used to add color and separation between my datatableFSC components and the navigationButtonFSC presents three custom buttons on the bottom of the screen.  The selected button determines what part of the Flow executes next.

The datatables are configured by providing the parameter values for the input and output sObject Collection Variables and each column is configured with parameters for field name, label and type along with optional parameters for alignment and width.

The final result gives me a Flow that I can use to view and interact with my tables.  I can sort any column in any table, I’ve added logic that allows me to select, then hide rows in any of the tables and I can select special processing to adjust the type of hierarchy used and then re-display the tables.

Here’s the Flow in action.

Youtube


See the complete series on how you can use the datatable Flow Screen Component in your Lightning Flows.

Part 1 – Use a Datatable to present a dynamic choice for record selection in a Flow.
Part 2 – Use a Datatable in a Flow to select and act on a collection of records.
Part 3 – Use a Datatable to display a formatted, interactive table in your Flow.
Part 4 – Use a Datatable to inline edit a group of records.




Enhance your Flows with Data Tables – Part 2

Lightning Flow Screen Components are a great way to add power to and improve the look of your Flows.  In this four -part series, I’ll show you how a single component, designed to display a list or table, can enhance your Flows.

The unofficialsf.com website includes datatableFSC as one of many Flow Screen Components you can use for your own organization.

This component is derived from the datatable Lightning Base Component.  I recently enhanced the base component to be more powerful and Admin friendly.

I will be showing you some examples about how the Datatable component:

  • Can be used in place of a Record Choice Set (Dynamic Record Choice) for selecting a record while displaying more than a single field
  • Can be used to select multiple records from an object then perform additional actions on them
  • Can be used to present your data in an interactive and visually enhanced format
  • Can be used to display and inline edit a collection of records

Part 2: Acting on a Group of Records

In my company, we use Email to Case to generate Cases for our support team.  An issue that comes along with enabling this feature, is that we tend to get quite a few Cases created as the result of junk emails.  We don’t want just anybody to be able to delete a Case, so we have a process where any user can ‘flag’ a junk Case for deletion. A manager can then run a Flow that shows all of the flagged Cases and can make a final review before actually deleting the Cases.

I used the datatableFSC Flow Screen Component to present the list of flagged Cases and the Flow uses the collection of records selected by the manager to handle the deletions.

See https://unofficialsf.com/datatable for complete information and instructions on this component.

By not entering any values for the width or alignment parameters, I let the system use its defaults to handle the columns widths and alignments.

Only fieldName, label and type are Required
Default Table Format

The records presented in the table are stored in the {!colCase} variable by the Get Cases Selected (Fast Lookup) node.

The datatableFSC component uses that collection variable to build and display the records for the table.

By defining an Output Parameter, the records selected by the user are passed back to the flow in the {!colCaseDelete} variable.

That variable is then used in the Delete Cases (Fast Delete) node.

The datatableFSC component provided a great way to present a formatted view of a list of records, select one or more from that list, and then use the flow to act on the selected records.


See the complete series on how you can use the datatable Flow Screen Component in your Lightning Flows.

Part 1 – Use a Datatable to present a dynamic choice for record selection in a Flow.
Part 2 – Use a Datatable in a Flow to select and act on a collection of records.
Part 3 – Use a Datatable to display a formatted, interactive table in your Flow.
Part 4 – Use a Datatable to inline edit a group of records.



Enhance your Flows with Data Tables – Part 1

Lightning Flow Screen Components are a great way to add power to and improve the look of your Flows.  In this four -part series, I’ll show you how a single component, designed to display a list or table, can enhance your Flows.

The unofficialsf.com website includes datatableFSC as one of many Flow Screen Components you can use for your own organization.

This component is derived from the datatable Lightning Base Component.  I recently enhanced the base component to be more powerful and Admin friendly.

I will be showing you some examples about how the Datatable component:

  • Can be used in place of a Record Choice Set (Dynamic Record Choice) for selecting a record while displaying more than a single field
  • Can be used to select multiple records from an object then perform additional actions on them
  • Can be used to present your data in an interactive and visually enhanced format
  • Can be used to display and inline edit a collection of records

Part 1: Selecting a Record

When I first built this flow, I used a Record Choice Set (Dynamic Record Choice) in my screen to present a list of records to make a selection from.

A common issue Admins run into with this type of input is that one is restricted to displaying just a single field from the record when presenting the selection.  Like many Admins, I created a new custom field in my object to combine the values of multiple other fields so I could show more information in my selection prompt.

Custom Formula Field for my Record Selection
Formula

This type of selection is awkward and you are not able to make the information line up from one record to the next.


Here’s what happens when I replace the Record Choice Selection with a Datatable Flow Screen Component.

See https://unofficialsf.com/datatable for complete information and instructions on this component.

My Flow looks up a collection of records to display in the table and passes that parameter to the component.

Note: Even though you are displaying a table for a single object, parameters are available for 8 standard or custom objects. This is to allow an Admin to easily use this component with no Lightning Component source code customization required.

sObject Collection Variable with records to display in the table

I define the structure of my table by providing field and formatting information for up to 10 columns.

Column Parameters – The first column can display an icon
Column Parameters – Field Name, Label & Type with Width & Alignment

Column Parameters – Only fieldName, label and type are required

When the user clicks Next, the component can pass a collection of the selected records back to the Flow.

Output Parameter – sObject Collection Variable with record(s) selected from the table

Now the user sees a formatted table with a checkbox available to make their selection.

Selecting a record with a datatable

The Flow then uses the values it needs from the selected record to complete its processing.


See the complete series on how you can use the datatable Flow Screen Component in your Lightning Flows.

Part 1 – Use a Datatable to present a dynamic choice for record selection in a Flow.
Part 2 – Use a Datatable in a Flow to select and act on a collection of records.
Part 3 – Use a Datatable to display a formatted, interactive table in your Flow.
Part 4 – Use a Datatable to inline edit a group of records.



Load a Visualforce Page with Parameters in Lightning

I was looking to display Visualforce pages using the RecordId inside of my Lightning Record pages. After finding a very elegant solution to pass the RecordId from a Lightning Component container to a Visualforce page displayed in an iframe I decided it was time to move the solution from the Sandbox into Production.

What worked perfectly in the Sandbox gave me nothing but a blank component in Production. After spending way too much time trying to find a solution, I gave up and went to Plan B.

SANDBOX
PRODUCTION

Plan B

I was able to use a Lightning Component to navigate to a URL, so I built a component to build the URL for the Visualforce page including the parameter for the RecordId of the page.

    doInit : function(component, event, helper) {
        var params = new Array();
        params.push(component.get('v.recordIdVar') + '=' + component.get('v.recordId'));
        params.push(component.get('v.otherParams'));
        component.set('v.queryString', params.join('&'))
    },

    openPage : function(component, event, helper) {

        // Show spinner once button is pressed
        var spinner = component.find('spinner');
        $A.util.removeClass(spinner, 'slds-hide');

        // Build url for Visualforce page
        var vfURL = 'https://' + component.get("v.domain") + '--c.visualforce.com/apex/';
        vfURL = vfURL + component.get("v.pageName") + '?';
        vfURL = vfURL + component.get("v.queryString");
        console.log("VF URL: ",vfURL);

On a standard Lightning page, the component switches from the Record page to the Visualforce page, returning to the Record page when the Visualforce page exits.

                // Handle non-console user
                console.log("Not in Console");
                var urlEvent = $A.get("e.force:navigateToURL");
                urlEvent.setParams({
                     "url": vfURL
                });
                urlEvent.fire();

It is a bit different on a Console page where the Visualforce Page is loaded in a new Tab which reverts to the Record page Tab when exiting. This normally leaves a second “dead” Record page Tab behind so I made sure the component saved the ID of the Calling Tab or Subtab and closed the “dead” tab when finishing with the Visualforce page.

// Check to see if running in a Console
    var workspaceAPI = component.find("workspace");
    workspaceAPI.isConsoleNavigation().then(function(consoleResponse) {            
        console.log("IsConsole: ", consoleResponse);
        if (consoleResponse) {

            // Save current tab info
            workspaceAPI.getFocusedTabInfo().then(function(tabResponse) {
                var closeTabId = tabResponse.tabId;
                var closeTitle = tabResponse.title;
                var parentTabId = tabResponse.parentTabId;
                var isSubtab = tabResponse.isSubtab;
                console.log("Current Tab: ", closeTabId + " | " + closeTitle);
                console.log("Is Sub: ",isSubtab," ParentId: ",parentTabId);

                // Open Visualforce Page in a new tab
                if (isSubtab) {
                    workspaceAPI.openSubtab({
                        parentTabId: parentTabId,
                        url: vfURL,
                        focus: true
                    }).then(function(openSubResponse) {
                        console.log("New SubTab Id: ", openSubResponse);
                    })
                    .catch(function(error) {
                        console.log(error);
                    });                        
                } else {
                    workspaceAPI.openTab({
                        url: vfURL,
                        focus: true
                    }).then(function(openParResponse) {
                        console.log("New ParentTab Id: ", openParResponse);
                    })
                    .catch(function(error) {
                        console.log(error);
                    });                        
                }

                // Because exiting the VF page will reopen the object record,
                // close the tab we started on
                if (tabResponse.closeable && !tabResponse.pinned) {
                    workspaceAPI.closeTab({
                        tabId: closeTabId
                    }).then(function(closeResponse) {
                        console.log("Closed: ", closeTitle);                      
                    })
                    .catch(function(error) {
                        console.log(error);
                    });                            
                } else {
                    console.log("Left Open: ", tabResponse.title);
                }
            })
            .catch(function(error) {
                console.log(error);
            })

Rather than load the Visualforce page immediately, I display a button. On the button press, the component loads the Visualforce page.

<aura:component implements="flexipage:availableForAllPageTypes,force:hasRecordId" access="global">

    <lightning:workspaceAPI aura:id="workspace" />

    <aura:attribute name="buttonLabel" type="String" />
    <aura:attribute name="buttonVariant" type="String" default="neutral" />
    <aura:attribute name="domain" type="String" />
    <aura:attribute name="pageName" type="String" />
    <aura:attribute name="recordIdVar" type="String" />
    <aura:attribute name="otherParams" type="String" />

    <aura:attribute name="queryString" type="String" />

    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />

    <lightning:card>
        <lightning:layoutItem padding="around-small">
            <lightning:button label="{!v.buttonLabel}" variant="{!v.buttonVariant}" onclick="{!c.openPage}" />
            <lightning:spinner aura:id="spinner" alternativeText="Loading" variant="brand" size="large" class="slds-hide" />
        </lightning:layoutItem>
    </lightning:card>

</aura:component>

I’ve made the component as generic as possible by providing the following attributes one can use when placing the component on their Lightning Record page.

  • Button Label (Text to appear on the Button)
  • Button Variant (Button styling: base, neutral, brand, destructive, success)
  • Domain Name (Your Production or Sandbox domain name)
  • Visualforce Page Name (The API Name of the Visualforce Page)
  • RecordId Parameter Name (The name of the URL parameter for the RecordId)
  • Additional URL Parameters (Any other URL parameters & values)

Here’s the Component in Action

Find More at Unofficial SF

Visit Unofficial SF (Load Visualforce Page) to get the source code for this component and some of my other Lightning Components, Visual Flow Screen Components, Visual Flow Actions and much more.

Some of my Other Lightning Page Components

Close Console Tabs

Winter ’19 now allows you to close all non-pinned Lightning Console tabs (Shift-W). You can install this component to provide a Utility Bar option to close all your opened tabs and even specify whether or not to include pinned tabs.

Field Update Button

This is a generic Lightning Component that can be used in place of a JavaScript button to set the value of a single field in a record. This is handy for using a button to trigger a Process Builder.

Large Text Area Component

This is a generic Lightning Component that allows for the display or editing of a Long Text Area field on the record page with a larger input box than what is standard and the ability to toggle it on and off.

Quick Action Button

This is a generic Lightning Component that allows a Button to execute a Quick Action. The Button can be placed anywhere on a Lightning Record Page.

Toggle Field Button

This is a generic Lightning Component that allows a Toggle Button to set/clear a checkbox field. The Button can be placed anywhere on a Lightning Record Page.

Some Flow Screen Components I’ve Enhanced

The Navigation Button component has been updated to support dual buttons.  Think OK/Cancel or Previous/Next. Navigation Button

Now you can create interactive images and 3D buttons – Image Button

Filters, Parent/Child and more for the flow screen Lookup component.

Close all Tabs in a Lightning Console

CloseAllTabs

There is an idea on the Idea Exchange about looking for a way to close all tabs in a Lightning Console App.  It’s an existing option in Classic that hasn’t yet made its way to Lightning.

Lightning Console: Close all tabs option

I’ve spent way too much time manually closing tabs and wanted a way to make this easier.

I stumbled across a reference to a component that could be used to refresh a tab in a console and saw that it used a reference to lightning:workspaceAPI.  I was able to use this Lightning Console JavaScript API to query the open tabs in a console view and choose which ones I wanted to close.

There is some debate in the comments on the idea exchange about whether or not to leave pinned tabs open when performing a Close All tabs.  I designed my component to take a single parameter to specify if pinned tabs should be closed or left open.

It’s easy to implement on the Utility Bar.

UtilityBarSetup

When selected, it pops up a Close Tabs button and the component takes care of the rest.

UtilityBar

The component consists of three parts:

  1. Markup (closeConsoleTabs.cmp)
  2. JavaScript Controller (closeConsoleTabsController.js)
  3. Design (parameter) File (closeConsoleTabs.design)

You can get the complete source code and installation instructions here:

GitHub

Check it out and take advantage of a great time saver.

Implementing a Closed/Lost Reason on Opportunity

sales-ops-solutions-comic-strip-lost-the-deal1

A recent blog post by Jared Baker of Cloud My Biz suggests including a Lost Reason picklist field on your Opportunities to assist with understanding why some deals don’t close and allowing you to focus on better quality deals.  Here’s an overview of how I have implemented that for my Sales team.

Create a Picklist of Closed/Lost Reasons

Choose reasons that are meaningful to your organization and will provide useful groupings for your Reports and Dashboards.  Here’s my complete picklist for Lost/Cancelled Reason:

l1

In addition to a custom picklist field for Lost/Cancelled Reason, I also added these fields:

Field Type Description
Trigger_Lost__c Checkbox Use button/link to set this field to trigger the Closed Lost Process Builder and control the Reason picklist values
Closed_Lost_Notes__c Text Area Additional details on why the deal was lost
Competitor_Lost_To__c Picklist List of competitors

Special Processing Note:

I share the Visualforce page and fields outlined in this post to also handle reasons we might Cancel an Opportunity after it has been Closed Won rather than Lost.  The Closed_Lost_Reason__c field is a Dependent Picklist field controlled by the value of Trigger_Lost__c.  I control what selections are available based on the value of the Trigger Lost field.

capture

For more information on Dependent Picklists see:

Dependent Picklists

Try It Out: Create a Dependent Picklist

Add a Custom Button to Trigger the Lost Processing

gwi-opportunity-batchelder-brothers-hpbx-salesforce-enterprise-edition

My Lost button executes some JavaScript that sets my Trigger Lost field to True and opens a custom Visualforce page to collect data for my Reason, Notes and Competitor fields.

{!REQUIRESCRIPT("/soap/ajax/31.0/connection.js")}

var myquery = "SELECT Id, Name, Trigger_Lost__c FROM Opportunity WHERE Id = '{!Opportunity.Id}' limit 1";

sforce.connection.sessionId = "{!$Api.Session_ID}";
result = sforce.connection.query(myquery);
records = result.getArray("records");

var myObject = records[0];
var updateRecord = new Array();

myObject.Trigger_Lost__c = true;
updateRecord.push(myObject);

result = sforce.connection.update(updateRecord);

if(result[0].getBoolean("success")){
window.location = "/apex/Opportunity_Lost?id=" + "{!Opportunity.Id}";
}else{
alert('Could not Trigger Process: '+result);
} 

Provide a Visualforce Page to Collect Details for your Lost Fields

I created a simple page with only the fields I wanted to capture when setting an Opportunity to Lost.

capture

<!--
Shared Visualforce page used to present and collect reason for Opportunity
being set to either Closed Lost or to Cancelled.
Closed Lost Reason is a dependent picklist based on the value of Trigger Lost

Eric Smith - GWI - May 2016
-->
<apex:page standardcontroller="Opportunity">
<apex:messages />
<apex:sectionheader title="{!$ObjectType.Opportunity.label} Edit" subtitle="Lost/Cancelled Opportunity"/>

<chatter:feedwithfollowers entityId="{!Opportunity.Id}"/>

<apex:form >
<apex:pageblock mode="edit" title="{!$ObjectType.Opportunity.label} Edit">
<apex:pageblockbuttons >
<apex:commandbutton value="Save" action="{!Save}"/>
<apex:commandbutton value="Cancel" action="{!Cancel}"/>
</apex:pageblockbuttons>

<apex:pageblocksection title="Lost/Cancelled Details" showheader="true" columns="1">
<apex:outputtext label="Stage" value="{!Opportunity.StageName}"/>
<apex:pageblocksectionitem />
<apex:inputfield value="{!Opportunity.Closed_Lost_Reason__c}" required="true"/>
<apex:pageblocksectionitem />
<apex:inputfield value="{!Opportunity.Closed_Lost_Notes__c}" required="false" style="width: 500px; height: 100px"/>
<apex:pageblocksectionitem />
<apex:inputfield value="{!Opportunity.Competitor_Lost_To__c}" rendered="{!Opportunity.Trigger_Lost__c}" required="true"/>
<apex:pageblocksectionitem />
<!-- This next field needs to be on the page for the dependent picklist to work -->
<!-- 0px keeps it from displaying or being changed -->
<apex:inputfield value="{!Opportunity.Trigger_Lost__c}" style="width: 0px; height: 0px"/>
</apex:pageblocksection>

</apex:pageblock>
</apex:form>

</apex:page>

Special Processing Note:

Competitor Lost To is not rendered if I’m processing a Cancel rather than a Lost and Trigger Lost is on the page because it controls the values displayed in the Closed Lost Reason picklist.

Adding More Control with a Validation Rule

I didn’t want my users to be able to select Other as their reason without explaining why so I added a Validation Rule requiring 50 or more characters in the Closed Lost Notes field if they selected Other as their reason.

capture

Process Builder kicked off by Trigger_Lost__c

When the Lost button Javascript executes, it set the Trigger_Lost__c checkbox field to True and saves the Opportunity record.  That record change causes my Stage: Set Closed Lost Process Builder to execute.  The only action I’m currently doing there is setting my Opportunity Stage to Closed Lost.

capturecapture2

How do I use this Data?

Here’s a monthly report I run that lists all of my Lost or Cancelled Opportunities.  It is organized by what Stage the Opportunity was in before it was changed to Lost.

gwi-lost-cancelled-opportunities-salesforce-enterprise-edition

Go ahead and give this one a try

Adding this feature to your org will give you some good practical experience with Custom Buttons, Visual Force pages, Dependent Picklists, Validation Rules, Process Builder and Reports.

Why this element should be first in all of your Visual Workflows

systema-spetsnaz-training-where-do-i-start

Have you ever wanted to temporarily disable a Visual Workflow?  How about wanting to debug your flow with some test data?

Add this simple Assignment Element to the beginning of your flow and you can run your flow with test data (1), disable your flow (2) or fully deploy your flow (3).

bi1

(1) Run your Flow with Test Data

In the Assignments section of the Assignment Element, provide a test data value for each of your flow’s input variables.

BI2.png

When you select Run, your flow will execute with your test data as input.

BI3.png

(2) Disable your Flow

If you want to temporarily keep your flow from executing, just remove the arrow linking your Assignment Element to the next Element.

BI4.png

Don’t worry about the General Warning messages about sections that are never used.  Your flow is still valid but only the Assignment Element will be executed, effectively disabling your flow.

bi5

(3) Deploy your Flow for normal usage

Select the first Element after your Assignment Element as the Starting Element and your flow will execute normally.

bi6

Again don’t worry about the Warning Message.  Your Flow is ready to go.

bi7

Special Admin Tip:

Update the description and save each of your modified flows as a new version so you can Activate whichever version of the flow you want at any time.

BI8.png

There you have it.  One simple Assignment Element at the beginning of your Visual Workflow and you can use it three different ways.