tag:goravseth.com,2014:/feedGorav Seth2023-11-13T10:21:53-08:00Gorav Sethhttps://goravseth.comSvbtle.comtag:goravseth.com,2014:Post/flow-http-callout-and-transform-element-demo2023-11-13T10:21:53-08:002023-11-13T10:21:53-08:00flow http callout and transform element <p><em>in preparation for a user group session with peter churchill re extending flow with http callouts and lwc for a dc user group meeting, i tested http callout and external services against the petfinder api, developer.trade.gov consolidated screening list api, and the boldsign api. my notes and observations re http callout and transform element are below. peter figured out a bunch of cool things</em></p>
<p>One of the first decisions to make is whether to use http callout or use external service. My conclusion is that if the endpoint provides an open api spec, it is easier to create an external service and select the methods you need in the flow, for the following reasons:</p>
<ul>
<li>when you create a http callout action, you paste in the response and then salesforce parses it. in this process, you have to go through and manually set the types (string, date, boolean, etc) for any properties that the system is not able to interpret (typically because they are blank/null in the response). the types would be specified in the open api spec, so you wont have to deal w this.<br>
</li>
<li>the response you parse may not be accurate for different queries. I created the http callout action against the consolidated screening list api using a response for the query ‘vladimir putin’. this worked fine for a bunch of tests. then i tested the query ‘yep’ - which has a ton of hits. this threw an unhandled fault for reasons I could not pin down. I then edited the callout to use the response for ‘yep’ and now it works for both vladimir putin or yep but it wont work for just ‘putin’. so overall this is less robust than external services, which is more of a firm contract.</li>
<li>if you plan to hit multiple endpoints on the api, you have to create a new http callout (which creates a new external service) each time. for example with boldsign, i wanted to get templates, select the template in a screen, and then send the template. this would be two different external services, instead of one service with multiple methods. there is a limit of 150 external service registrations, which is pretty high, but still worth considering.</li>
</ul>
<p>if you do not have an open api spec available, then the http callout is certainly worth trying. many platforms provide sample responses in their api documentation or you can easily copy it from postman. you can <em>fairly quickly</em>* create a http callout action in the flow without converting the response to an open api spec, which requires some expertise (note that <a href="https://swagger.io/tools/swaggerhub-explore/">swagger inspector, now known as swaggerhub explore</a> - makes it possible to generate open api spec more easily)</p>
<ul>
<li>For both http callout and external service, you first need to setup authentication via named credentials/external services, which can be fairly simple or can be complex, depending on the api. this is a separate topic unto itself. <a href="https://trailhead.salesforce.com/content/learn/projects/quick-start-create-http-callouts-with-flow-builder/create-the-credentials">this trail</a> has some helpful guidance.</li>
</ul>
<p>trade.gov and boldsign both offer api key authentication via a custom header, which named credentials makes very easy to implement. I use a single external credential with no authentication, and then add a custom header on the respective named credential with the api key.</p>
<p>for petfinder i used the client credentials flow, in a lucky or perhaps educated guess, which was a bit more complex to setup. </p>
<p><strong>now on to the fun stuff - poking on http callout and transform.</strong></p>
<p>when you make a callout, you typically need to send some information to the endpoint, such as the name of the person being screened, or the type of animal you want to view. external services handles this very well, but http callout makes it <em>almost</em> as easy to define query params or body params - just note that spelling and capitalization is on you!</p>
<p>the data returned from the callout typically is used for one of two things (or possibly to do both):</p>
<ul>
<li>display the results (ie in a screen flow or lwc)</li>
<li>create/update records in salesforce </li>
</ul>
<p><em>lets start with displaying data returned from the callout</em></p>
<p>depending on the response structure, you <em>may</em> be able to display the information you need directly in a displayText element in a flow screen. however collections / arrays in the response can only be accessed by looping over them* so you need to loop over the response, and add the content to be displayed to a text variable or text template that can be displayed in a screen after the loop (or use a screen within the loop). it is functional but not fancy, and may or may not be good enough depending on your use case.</p>
<p>*one note - flow collection choices in flow screens can handle collections/arrays in the response very well! in the boldsign example, I retrieved all templates, and mapped them directly to a collection choice in a flow screen, which displayed the label and had the ID as the value. worked marvelously. </p>
<p>if you want to display data from multiple items in a collection (such as a list of pets available for adoption), it is hard to make it look nice using pure flow. you can loop through the collection, add text to a variable with line breaks, and it works. for images, there is no way to display within flow out of the box. I used narender singh’s lwc to display an image via the URL, and it was usable, but if there are multiple images for one animal, you really are limited to clicking through each one on a separate screen, or maybe some hackish stuff with multiple image components on a single screen and conditional visibility depending on the size of the collection. </p>
<p>this is where it can be best to extend flow by using a custom LWC in the screen flow to display the response data. flow makes the ‘boilerplate’ part of making the callout simple - but there are limits, and using a lwc makes it much easier to parse and display the response how you need it.</p>
<p>you could code the callout also, but flow can easily pass the json response to the lwc**, so all the lwc needs to do is format the info for display which will save you some $$ and look like a million bucks</p>
<p>** one of the key insights Peter figured out is how to map the response from the callout directly to the LWC. It requires setting the type on the property to apex://ExternalService.APEXCLASSNAME</p>
<p>to get the apex class name, go to external services, find the callout/external service, go to the operation, and click on the chevron to select ‘more details’</p>
<p>use the apex class name for the 200 output. sample below.</p>
<p><a href="https://svbtleusercontent.com/s9DE63WA5E8NZos63VzVm20xspap.png"><img src="https://svbtleusercontent.com/s9DE63WA5E8NZos63VzVm20xspap_small.png" alt="Screenshot 2023-12-01 152157.png"></a></p>
<p>an example<br>
apex://ExternalService.callTradeDotGov_callTradeDotGov_OUT_2XX</p>
<p><em>now on to option 2 - using the response to create/update records in sfdc.</em></p>
<p>one idea i picked up from peter is to have the callout write the full json response to a text area on a record, and then do any further processing downstream asynchronusly. this way the callout will never fail due to parsing errors, validation rules, etc. however its not possible to map the full json response directly to a text field on an object with the transform element or with an assignment element. this requires invocable apex.</p>
<p>if we stay with what can be done solely in flow builder we have two options to take the response and create/update records in sfdc:<br><br>
a) use the transform element<br>
b) use an assignment element and if needed loops to process the response and map it to fields on objects. </p>
<p>from my testing, the possibilities for the two are the same. i did not find anything I could do with the transform element that I could not do with loops. there are some things you can do with loops that you cant do with the transform element (yet - it is in beta), or that may be easier/simpler in loops, at least for me.</p>
<p>for example, the syntax for the formula editor in the transformation is new to me and not well documented to my knowledge. I also did not see how to use the transform element to create a parent record (say an account) and 1 or more child records. I have done so using loops and collections in flow.</p>
<p>also flow does not currently offer an ‘upsert’ element, so if you need to match on existing records, you will want to use invocable / apex (and vote : <a href="https://ideas.salesforce.com/s/idea/a0B8W00000Gdf3zUAB/upsert-option-in-flow-builder">https://ideas.salesforce.com/s/idea/a0B8W00000Gdf3zUAB/upsert-option-in-flow-builder</a>)</p>
<p>CONCLUSIONS</p>
<ul>
<li>http callout and transform element work pretty well</li>
<li>they are complex and it takes time to build expertise </li>
<li>you need to understand the basics re data types + collections to work with it</li>
</ul>
<p>general info / tips / etc</p>
<p>if you make a mistake creating the callout, you can edit it by going to external services and clicking the chevron next to the operation and selecting ‘edit http callout action’ - but like external services it wont let you modify if the callout element is in use. so you may have to delete or modiffy the action in the flow first</p>
<p><a href="https://svbtleusercontent.com/vJcQCsoPdiir1FQe4hebXd0xspap.png"><img src="https://svbtleusercontent.com/vJcQCsoPdiir1FQe4hebXd0xspap_small.png" alt="Screenshot 2023-11-19 12.12.01 PM.png"></a></p>
<p>transform element</p>
<ul>
<li>in transform element, using a + worked to join strings together, && did not.</li>
<li>must map collection to sobject collection - then you can do stuff w it</li>
</ul>
<p>DEMO 1a - display</p>
<ul>
<li>developer.trade.gov consolidated screening api</li>
<li>create screen flow, add name field, create http callout action</li>
<li>show updating the null values after pasting the response in when creating action</li>
<li>debug the flow - show whats there</li>
<li>display the name, title, and link to source in the next screen using a loop</li>
<li>show what can be done with a relatively simple lwc in the flow - much nicer, slds et al.</li>
</ul>
<p>DEMO 1b - insert records<br>
instead of displaying, use transform element to insert tasks<br>
concatenate name + title + source in the transform element formula<br>
show issue with link <br>
do same thing with loop and assignments<br>
show that it works</p>
<p>old notes and stuff / fluff<br>
this requires an understanding of objects, arrays, and such things that I thought were exactly what this http callout / transform element were made to avoid. i certainly cant find a way to avoid them, though it is still in beta, so perhaps there are things coming that will break through this barrier.</p>
<p>?still need to re-test scheduled flow with wait element and ext service? does it work?</p>
<p>petfinder provides an open api spec at the end of the page (<a href="https://www.petfinder.com/developers/v2/docs/">https://www.petfinder.com/developers/v2/docs/</a>)</p>
tag:goravseth.com,2014:Post/making-cases-work-in-lightning2023-08-30T11:48:48-07:002023-08-30T11:48:48-07:00Making cases work in lightning<p>I couldnt find any clear documentation on how to get cases to just not be total crap in lightning. Tried many times, and failed miserably. finally found a way through lots of googling, and piecing things together. the short answer is that you need feed filters and a console app and then there is hope.</p>
<p>“Enable Case Feed Actions and Feed Items” in support settings</p>
<ul>
<li>otherwise you may find odd stuff with things not showing up in the case feed esp on old cases</li>
</ul>
<p>setup case feed filters</p>
<ul>
<li>can setup feed filters in classic, to determine what is on the page</li>
<li>feed filters only show up in console apps in lex, but actually make it usable. you can see all the emails, and easily move from one to the next. other normal things</li>
</ul>
<p>try “Enable Unread/Read on Compact Case Feed” - no idea what that does yet…and maybe ‘enable draft emails’ cuz why not.</p>
<p>create a console app, then do normal lighting app builder stuff. </p>
<ul>
<li>i went with details on left, and feed on right. equal split.<br>
</li>
<li>i stuck with case comments bc we have some notifications and such that I dont need to deal with rebuilding for now, though posts will be better in the long term.</li>
</ul>
<p>Create a custom send email action</p>
<ul>
<li>lets you set email template </li>
<li>try using junctionIdList to populate the to address if you have issues</li>
</ul>
<p><a href="https://svbtleusercontent.com/iDKDjHWfUKjQGMugKpBQgw0xspap.png"><img src="https://svbtleusercontent.com/iDKDjHWfUKjQGMugKpBQgw0xspap_small.png" alt="Screenshot 2023-08-30 144552.png"></a></p>
tag:goravseth.com,2014:Post/exporting-field-help-text-and-description-in-bulk2023-01-13T12:03:16-08:002023-01-13T12:03:16-08:00exporting field help text and description in bulk<p>TIL that you can create custom report type on entity definition + field definition to report on your fields!</p>
<p>h/t : <a href="https://www.asagarwal.com/how-to-report-on-objects-and-fields-defined-in-salesforce/">https://www.asagarwal.com/how-to-report-on-objects-and-fields-defined-in-salesforce/</a> and <a href="https://twitter.com/wellywoodanna">Anna Loughnan</a></p>
<p>However I could not include field help text, though it is listed in the <a href="https://developer.salesforce.com/docs/atlas.en-us.api_tooling.meta/api_tooling/tooling_api_objects_fielddefinition.htm">tooling api docs for fielddefinition</a></p>
<p>then i tried a custom report type on entity definition with entity particle, and added field definition via lookup</p>
<p>this worked in theory - i could include description and inline help text.</p>
<p>but when i ran the report, field description remained blank. other folks said the report type worked for them, so i should test more as the report types would be really simple to use. so try that first.</p>
<p>instead I ran a soql query to test it out, and this works</p>
<p><code class="prettyprint">select entitydefinition.label, inlinehelptext,fielddefinition.description,fielddefinition.label from entityparticle where entitydefinition.label = 'Opportunity'</code></p>
<p>and you can also include the defaultValue for the field by adding the DefaultValueFormula field</p>
<p><code class="prettyprint">select entitydefinition.label,inlinehelptext,fielddefinition.description,fielddefinition.label,DefaultValueFormula from entityparticle where entitydefinition.label = 'Opportunity'</code></p>
<p>there are a bunch more options, have not explored them all. see the docs and figure out what is useful for your use case!</p>
tag:goravseth.com,2014:Post/using-vscode-to-get-all-reports2022-12-21T09:39:14-08:002022-12-21T09:39:14-08:00using vscode to fix reports that break from configuration changes<p>changes to configuration, like updating picklist values, can easily mess up salesforce reports, which is a perennial problem for yours truly. i think sometime in the middle ages i dealt with this using eclipse but only vague memories remain.</p>
<p>working today on using vscode to deal with this…first I needed to get all reports locally.</p>
<p><a href="https://trailhead.salesforce.com/trailblazer-community/feed/0D54S00000A8b1PSAR">this post</a> on dev forum suggested a nifty <a href="https://marketplace.visualstudio.com/items?itemName=VignaeshRamA.sfdx-package-xml-generator">package generator extension</a></p>
<p>I used that extension to generate the package. check the “report” metadata type and it crunched through all 6k(!) reports we have and created a package.xml file</p>
<p><a href="https://svbtleusercontent.com/q3cZk2kB13AYsxRNYEdLcj0xspap.png"><img src="https://svbtleusercontent.com/q3cZk2kB13AYsxRNYEdLcj0xspap_small.png" alt="packagexmlreports.png"></a></p>
<p>then i could easily retrieve all reports by right clicking on the package.xml file and selecting “retreive source from org using manifest” in vscode!</p>
<p><a href="https://svbtleusercontent.com/8qAz7ioKHZb75oCHEhWmJG0xspap.png"><img src="https://svbtleusercontent.com/8qAz7ioKHZb75oCHEhWmJG0xspap_small.png" alt="retrievesource.png"></a></p>
<p>i then used find/replace in folder to update the old picklist value to the new one! but if I then use force:source:push it deploys <strong>all reports</strong>, not just the modified ones. </p>
<p>I found a way to only deploy modified reports : use sfdx force:source:tracking:reset <em>before</em> doing the find/replace to on reports</p>
<p>per the multiple warnings thrown all over this command, THIS CAN BE DANGEROUS AND YOU SHOULD BE CAREFUL</p>
<p>this command ‘resets’ the source tracking so it doesnt see any changes (like all the reports we just pulled in). but in this case that is just what i want.</p>
<p>after running the command, do find/replace in the reports folder in vscode to replace the old value with the new value.</p>
<p>then run force:source:push and only the reports that were just modified will be pushed (bc we had reset source tracking)!</p>
<p>however, if any of the reports you modified have other errors (invalid picklist values, deleted record types, etc)…those will throw an error and nix your deployment…</p>
<p><a href="https://svbtleusercontent.com/5gb6ftcGSPVkHST5asabq70xspap.png"><img src="https://svbtleusercontent.com/5gb6ftcGSPVkHST5asabq70xspap_small.png" alt="pushError.png"></a></p>
<p>but i simply copied those reports into .forceignore and did force:source:push again and SUCCESS!</p>
<p><a href="https://svbtleusercontent.com/eifrAhYEdeMwjMA4G17bwE0xspap.png"><img src="https://svbtleusercontent.com/eifrAhYEdeMwjMA4G17bwE0xspap_small.png" alt="successpush.png"></a></p>
<p>you can copy/paste the errors from terminal in vscode into excel and split text to columns to get the report names. you also can get the errors in deployment status in the target org</p>
<p>format for forceignore is reportname.report-meta.xml (no folder name)</p>
<p>docs:<a href="https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_exclude_source.htm">https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_exclude_source.htm</a></p>
<p>so that gets the reports to a sandbox, but how do you get them to prod (without deploying all reports, or manually selecting them in a change set…)</p>
<p>you <em>may</em> be able to use force:source:push and set -username to your production org. i dont think that will run tests and the deployment will fail, but have not tested yet.</p>
<p>i was testing out the new devops center functionality, and that worked like a charm. </p>
<p>list of steps</p>
<ul>
<li>refresh sandbox</li>
<li>vscode:
<ul>
<li>create package.xml using package generator</li>
<li>right click on package.xml and select ‘retrieve source in manifest from org’</li>
<li>run sfdx force:source:tracking:reset (must be done before find replace!)</li>
</ul>
</li>
<li>devops center: (not sure if this can be done before force:source:tracking:reset or not, so doing after)
<ul>
<li>add environment </li>
<li>create new work item</li>
</ul>
</li>
<li>vscode
<ul>
<li>find/replace in vscode for old field -> new field</li>
<li>run sfdx force:source:push</li>
</ul>
</li>
<li>devops center:
<ul>
<li>pull changes into work item (will be just the reports that were modified)</li>
<li>deploy updated reports to prod via devops center</li>
</ul>
</li>
</ul>
tag:goravseth.com,2014:Post/refreshing-sandboxes-when-direct-login-is-blocked2022-12-21T07:41:03-08:002022-12-21T07:41:03-08:00refreshing sandboxes when direct login is blocked<p><strong>edit : looks like the link in the sandbox refresh success email already has the right url to allow you to login with username and password.</strong></p>
<p>file under stupid workarounds that cant possibly exist in a 50B platform</p>
<p>if you use single sign on in prod, and block login from login.salesforce.com because security and all, refreshing sandboxes becomes a stupid pita because the new sandbox inherits the ‘block login from’ setting,</p>
<p>therefore after refresh is complete, you click ‘login’ in the sandbox list from prod, and it takes you to the direct url to login (ie cs13.salesforce.com)</p>
<p>enter your pwd and you get this helpful message</p>
<p>Please check your username and password. If you still can’t log in, contact your Salesforce administrator.</p>
<p>To fix this, you have to manually craft the mydomain url for your sandbox. this is formatted as</p>
<p>prodMyDomain–sandboxName.sandbox.lightning.force.com</p>
<p>in my case, prod my domain = ashoka<br>
sandbox name = oppasn</p>
<p>so the sandbox my domain url = <br>
<a href="https://ashoka--oppasn.sandbox.lightning.force.com/">https://ashoka–oppasn.sandbox.lightning.force.com/</a></p>
<p>now you are not logging in from test.salesforce.com, you are logging in from mydomain url, so you can complete the login.</p>
tag:goravseth.com,2014:Post/updating-sort-order-on-campaign-member-status2022-12-07T10:57:13-08:002022-12-07T10:57:13-08:00updating sort order on campaign member status<p>quick note re updating sort order on campaign member status</p>
<p>you cant do this via the UI, you have to do a data load</p>
<p>and sort order is unique, you cant set a member status to have a sort order of 2 if another one already is 2.</p>
<p>so lets say you have 10 member statuses, with sort order 1-10.</p>
<p>if you want to update them, query via workbench</p>
<p>select id,label,sortOrder from campaignMemberStatus where campaignId = ‘insert ID of your campaign here’</p>
<p>then to update the sort order, you need to set the new ones starting with 20, so you have 20-30</p>
<p>then the update will work</p>
<p>else you will get an error</p>
tag:goravseth.com,2014:Post/flow-rich-text-email-with-list-of-records2022-09-16T10:24:41-07:002022-09-16T10:24:41-07:00flow rich text email with list of records<p>Spring21 brought us <a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_forcecom_flow_fbuilder_send_rich_emails.htm&release=230&type=5">rich text emails in flow</a>, which made it fairly simple to create a nice looking email that includes a list of records. throw it in a scheduled flow and you have some serious potential. add a link to a report with inline editing enabled and you have black belt ninja material!</p>
<p>my use case : send opportunity owners a list of their open opportunities that have passed the close date.</p>
<p>here is the full flow<br>
<a href="https://svbtleusercontent.com/bzjDWcxNvN56v4DYST627b0xspap.png"><img src="https://svbtleusercontent.com/bzjDWcxNvN56v4DYST627b0xspap_small.png" alt="fullFlow.png"></a></p>
<p><strong>will break down each piece below</strong></p>
<p><em>Schedule:</em> the flow is scheduled on the user object, and fires for all users where isActive = true (if the owner is inactive, nobody will be notified. I would handle these through a separate process.)</p>
<p><em>getOpportunities:</em> get all opportunities, where ownerid = $record.id (the user from the scheduled flow) and closeDate is less than $flow.current date</p>
<p><em>getReport:</em> In the email, I include a link to a report so they can edit it using inline editing for reports (while I dream of the day when engineering allows <a href="https://ideas.salesforce.com/s/idea/a0B8W00000J6QqlUAF/report-inline-editing-for-amount-and-quantity">editing of currency fields in reports</a>). So instead of hard coding that link, I query for the report, since i built in sandbox, and you know you shouldn’t hard code things…</p>
<p><em>asstOppCount:</em> used to set a variable to count the number of opps found which is used in the decision / decGotOpp to determine whether to proceed. I can never remember what the ‘right’ way is to check if a lookup found any records. And you cant schedule a flow to run monthly, so you run it daily for all users and do nothing on all but one day per month.</p>
<p><em>sortOpps:</em> get records only let you sort by one parameter, thankfully this lets me sort by close date, and then by name, so the opps are in the right order in the table</p>
<p><em>asstInitTable:</em> starts the table and inserts the header row. The new flow datatable element is just for screens, but I found <a href="https://forcepanda.wordpress.com/2021/03/23/how-to-send-table-in-emails-via-flow/">this great article</a> on including tables in flow emails. Some <em>very</em> basic html required here, but should be able to <a href="https://www.w3schools.com/html/html_tables.asp">teach yourself</a> in 15 mins</p>
<p>the html for the table that is used in the email is stored in a text variable, which I have named <em>vaEmailTableBody</em></p>
<p><em>vaEmailTableBody</em> is assembled by using text templates to store pieces of html, and adding the templates to <em>vaEmailTableBody</em> in assignment elements to create the full html table. </p>
<p>Text templates allow you to type straight html and merge fields without using quotes for strings and joining pieces together with & like you need to in a formula field (the forcepanda example uses formula fields, which is why my example looks simpler). ** Make sure to set ‘view as plain text’ on the text template as they default to rich text.** </p>
<p>I use <em>ttTableAndHeader</em> to create the table and to set the headers for the columns. It includes a wrapper div and some very simple css to help format the table to look better in the email. I include opportunity name, close date, stage, and amount. You can tweak and add more columns as you see fit!</p>
<p><a href="https://svbtleusercontent.com/qC8RMCGNeSdP16aWatQaT20xspap.png"><img src="https://svbtleusercontent.com/qC8RMCGNeSdP16aWatQaT20xspap_small.png" alt="textTemplateTableHeader.png"></a></p>
<p>if you want to copy/paste the content of the text template, you can do so <a href="https://gist.github.com/goravseth/468335755fef7994cef004e69b7b47f3">here</a></p>
<p>an assignment element <em>asstInitTable</em> adds this text template to the <em>vaEmailTableBody</em> variable. </p>
<p><a href="https://svbtleusercontent.com/9S9rypg2Fevwi18rF4L8HM0xspap.png"><img src="https://svbtleusercontent.com/9S9rypg2Fevwi18rF4L8HM0xspap_small.png" alt="initTable.png"></a></p>
<p>I also set a number variable in the assignment, which will be incremented for each row of the table to create a stripey row effect without fancy css that probably wont work in emails anyways.</p>
<p>Now that the table is created with 4 columns, we loop over the collection of opportunities owned by this user. Within the loop, for each opportunity record we add a row to the table with the data from the opportunity.</p>
<p><em>loopOps:</em> loop over each opportunity record, so that we can add the actual data from each opp to the table to a separate row.</p>
<p><em>asstBuildTable:</em> uses another text template (<em>ttTableRow</em>) to add a row of content to the table, and increments the row number. </p>
<p><a href="https://svbtleusercontent.com/kN4t9pfKLoqrsmwJGFAhFA0xspap.png"><img src="https://svbtleusercontent.com/kN4t9pfKLoqrsmwJGFAhFA0xspap_small.png" alt="asstBuildTable.png"></a></p>
<p>ttTableRow starts a table row, adds each of the columns, and closes the table row. the merge fields in ttTableRow bring in opportunity name, close date, stage, and amount. if you want to copy and paste the content of the ttTableRow, you can do so <a href="https://gist.github.com/goravseth/0fcbd263cc2e34894de5d29b98295665">here</a> </p>
<p><a href="https://svbtleusercontent.com/e9NZCRjR4wkxeEGmsuFPhs0xspap.png"><img src="https://svbtleusercontent.com/e9NZCRjR4wkxeEGmsuFPhs0xspap_small.png" alt="ttTableRow.png"></a></p>
<p>Note that I set opportunity name to also link to the record, and avoid hard coding the link by using the ‘partner url’ approach as <a href="https://salesforcetime.com/2022/09/13/how-to-create-a-record-link-in-flow/">documented here</a></p>
<p>{!ffBackgroundColor} is where i get fancy and change the background color of odd vs even rows. the stripey row effect happens via this line<br>
<code class="prettyprint"><tr style="background-color: {!ffBackgroundColor}; border: 1px solid #{!ffBackgroundColor};"></code></p>
<p>ffBackgroundColor calculates if its an odd or even row, and sets the color <code class="prettyprint">IF(MOD({!vaRowNumber},2) = 0, "#f2f2f2", "#ffffff")</code></p>
<p>The loop will add a row to the table for each opportunity. Once we have looped through all opportunities, we close the table, and send the email</p>
<p><em>asstCloseTable:</em> adds and directly to <em>vaEmailTableBody</em> - no text template required as that is all we need.</p>
<p><a href="https://svbtleusercontent.com/vix4tcgiGRqFecfRSeCUGf0xspap.png"><img src="https://svbtleusercontent.com/vix4tcgiGRqFecfRSeCUGf0xspap_small.png" alt="asstCloseTable.png"></a></p>
<p>the email body itself is constructed using a text template (<em>ttEmailBody</em>), which contains <em>vaEmailTableBody</em> at the spot where I want to insert the list of opportunities</p>
<p><a href="https://svbtleusercontent.com/52bXfcsWdRKsSC7bV8yqU40xspap.png"><img src="https://svbtleusercontent.com/52bXfcsWdRKsSC7bV8yqU40xspap_small.png" alt="ttEmailbody.png"></a></p>
<p>finally the send email action sends the email, with <em>ttEmailBody</em> as the body, and rich text email body set to true.</p>
<ul>
<li>i am able to send as an orgWideEmailAddress from a scheduled flow though it was not clear to me from the docs if that was going to work</li>
<li>i use a formula field for the recipient email with a variable to ensure all my tests are sent to a testing email address, so if i set vaIsDebug to true, it sends to the test address.</li>
</ul>
<p><a href="https://svbtleusercontent.com/7HauTA8ZCaXqoZbkVt6QHD0xspap.png"><img src="https://svbtleusercontent.com/7HauTA8ZCaXqoZbkVt6QHD0xspap_small.png" alt="ttEmailbody.png"></a><br>
.<br>
you can test this by clicking debug, but make sure the recipient email address is hard coded so you wont send to all your users</p>
<p>then set a schedule, activate, and go take a nice long walk…</p>
<p><a href="https://svbtleusercontent.com/cNU8Ff9D8tKxAQ6pH55grx0xspap.png"><img src="https://svbtleusercontent.com/cNU8Ff9D8tKxAQ6pH55grx0xspap_small.png" alt="openOppEmail.png"></a></p>
<p>here is a package you can use to install this into a sandbox and test it out yourself. use this at your own risk.</p>
<p>link to package : <a href="https://test.salesforce.com/packaging/installPackage.apexp?p0=04t6S00000160n1">https://test.salesforce.com/packaging/installPackage.apexp?p0=04t6S00000160n1</a></p>
tag:goravseth.com,2014:Post/bulk-deleting-reports-from-salesforce2022-09-02T07:00:03-07:002022-09-02T07:00:03-07:00bulk deleting reports from salesforce<p>i swear i used to have a way of deleting reports via the metadata api in bulk many years ago but i could not get that to work </p>
<p>this works:</p>
<p>query reports via workbench to csv</p>
<p>run delete - but not via bulk api - as delete operation not supported on bulk api</p>
<p>so you can do about 1-2 k at a time, vs the native interface which gives you 250, so its better</p>
<p>works for private reports also by adding using SCOPE allPrivate (and ensuring you have the perms to do so) see : <a href="https://help.salesforce.com/s/articleView?id=000317823&type=1">https://help.salesforce.com/s/articleView?id=000317823&type=1</a></p>
<p>works for reports </p>
<p>SELECT Id, Owner.Name FROM Report USING SCOPE allPrivate WHERE Owner.IsActive = false limit 1200</p>
<p>and for dashboards</p>
<p>SELECT Id, RunningUser.Name, Title FROM Dashboard USING SCOPE allPrivate WHERE RunningUser.IsActive = false limit 1200</p>
tag:goravseth.com,2014:Post/truncating-case-description-in-case-assignment-email2022-07-22T10:11:18-07:002022-07-22T10:11:18-07:00truncating case description in case assignment email<p>sure its 2022 and we have force lightning powers that emperor palpatine himself could only dream of, but if you want a good case assignment email, you need visualforce.</p>
<p>i rolled up a nice visualforce email template years ago that includes the case commments - sorted by date! </p>
<p>but the template includes case description, and sometimes people initiate a case via a gigantic email, and all of that email thread would come through on the visualforce assignment email</p>
<p>so i just need to truncate the length of the description.</p>
<p>the original<br>
<code class="prettyprint"><apex:outputField value="{!relatedTo.Description}"></apex:outputField></code></p>
<p>tried inserting a left in there, could not get anything to save<br>
examples<br>
<a href="/apex:outputField">/apex:outputField</a></p>
<p>apex:outputText would lose the formatting, regardless of how I tried to work with it, like</p>
<p><br>
<a href="/apex:outputText">/apex:outputText</a></p>
<p>I even tried abbreviating the string in a vf component, but outputText would lose the formatting</p>
<p>But to work with apex:outputField you need a sObject.</p>
<p>So in my custom component, instead of returning a string, I return a case sObject, with a truncated description field, and used apex:outputField</p>
<pre><code class="prettyprint">public with sharing class SortedCaseCommentsController {
public Id AttributeCaseId {get; set;}
public List<CaseComment> getCaseComments() {
return [
SELECT CommentBody, CreatedById, CreatedBy.Name, CreatedDate
FROM CaseComment
WHERE ParentId =: this.AttributeCaseId
ORDER BY CreatedDate DESC LIMIT 3
];
}
public case getCaseDescription(){
IF(this.AttributeCaseId != NULL){
list<Case> caseList = new list<Case> ([select description from Case where Id =: this.AttributeCaseId ]);
caseList[0].description = caseList[0].description.abbreviate(1500);
return caseList[0];
}
ELSE{return null;}
}
}
</code></pre>
tag:goravseth.com,2014:Post/marking-fields-used-in-integrations2022-04-21T11:00:39-07:002022-04-21T11:00:39-07:00Marking fields used in Integrations<p><em>Another way to mark fields used in Integrations.</em></p>
<p>2 years back I <a href="https://goravseth.com/protecting-fields-used-in-integrations">conjured up this approach</a> to prevent deletion of fields used in integrations.</p>
<p>I still needed a better way to flag these fields. Description field is pretty good, but wanted to add the picklist value “Integrated” to the <code class="prettyprint">field usage</code> picklist.</p>
<p>Turns out that you can only do this via the metadata api, so this is how to do it</p>
<ul>
<li>create a file named package.xml with the following content</li>
</ul>
<pre><code class="prettyprint"><Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>FieldBusinessStatus</members>
<name>StandardValueSet</name>
</types>
<version>54.0</version>
</Package>
</code></pre>
<ul>
<li>login to workbench.developerforce.com and go to migration - retrieve</li>
<li>upload that package.xml file </li>
<li>click ‘download zip file’</li>
</ul>
<p>this downloads the existing values for Field Usage picklist, in case someone has customized it before you!</p>
<ul>
<li>Unzip the file</li>
<li>Edit the file named “FieldBusinessStatus.standardValueSet”</li>
</ul>
<p>add the following lines to wherever you want the picklist to appear</p>
<pre><code class="prettyprint"> </standardValue>
<standardValue>
<fullName>Integrated</fullName>
<default>false</default>
<label>Integrated</label>
<groupingString>Active</groupingString>
</standardValue>
</code></pre>
<p>if you have not customized the values before, your file should look something like this</p>
<pre><code class="prettyprint"><?xml version="1.0" encoding="UTF-8"?>
<StandardValueSet xmlns="http://soap.sforce.com/2006/04/metadata">
<groupingStringEnum>FieldBusinessStatusCode</groupingStringEnum>
<sorted>false</sorted>
<standardValue>
<fullName>Active</fullName>
<default>false</default>
<label>Active</label>
<groupingString>Active</groupingString>
</standardValue>
<standardValue>
<fullName>Integrated</fullName>
<default>false</default>
<label>Integrated</label>
<groupingString>Active</groupingString>
</standardValue>
<standardValue>
<fullName>DeprecateCandidate</fullName>
<default>false</default>
<label>DeprecateCandidate</label>
<groupingString>DeprecateCandidate</groupingString>
</standardValue>
<standardValue>
<fullName>Hidden</fullName>
<default>false</default>
<label>Hidden</label>
<groupingString>Hidden</groupingString>
</standardValue>
</StandardValueSet>
</code></pre>
<ul>
<li>go up one folder, and select the package.xml file and the folder named “standardValueSets” and compress them into a single zip file</li>
</ul>
<p><a href="https://svbtleusercontent.com/vCxA3AGEhxX6Uv5crz8EWe0xspap.png"><img src="https://svbtleusercontent.com/vCxA3AGEhxX6Uv5crz8EWe0xspap_small.png" alt="folderstructure.png"></a></p>
<ul>
<li><p>login to workbench.developerforce.com and go to migration - deploy</p></li>
<li><p>select the zip file, and check allow missing files, ignore warnings, rollback on error, single package, and for test level select run local tests.</p></li>
</ul>
<p><a href="https://svbtleusercontent.com/fb9rbNjR1kv6wxmbNKr9LW0xspap.png"><img src="https://svbtleusercontent.com/fb9rbNjR1kv6wxmbNKr9LW0xspap_small.png" alt="Screenshot from 2022-04-21 14-23-48.png"></a></p>
<ul>
<li>click next, and if it deploys successfully, you will now have the ‘integrated’ value available to select in the Field Usage picklist!</li>
</ul>
<p><a href="https://svbtleusercontent.com/hdkFusEg8upjrv1boscCAf0xspap.png"><img src="https://svbtleusercontent.com/hdkFusEg8upjrv1boscCAf0xspap_small.png" alt="integrated.png"></a></p>