Lightning-Flow-Scanner / lightning-flow-scanner-core

A rule engine capable of conducting static analysis on the metadata associated with Salesforce Lightning Flows, Process Builders, and Workflows.
https://www.npmjs.com/package/lightning-flow-scanner-core
MIT License
27 stars 9 forks source link

Update same record in AfterUpdate Flow #21

Open nvuillam opened 1 year ago

nvuillam commented 1 year ago

IT's not recommended to update a record in a flow that has been triggered by an update of the same record

Updates should be performed in BeforeUpdate flows

Note: in such cases, it's necessary to do that, so the rule must be deactivatable for a given flow (in config file)

cc @RubenHalman

nvuillam commented 7 months ago

@RubenHalman unfortunately my knowledge about flows is not high enough to have the answer, but I'll try to find someone who knows :)

Precision: such rule would come from well-architected recommendation Takeaway #2: Stop putting same-record field updates into Workflow Rules and Process Builder. Start putting same-record field updates into before-save flow triggers instead.

https://architect.salesforce.com/decision-guides/trigger-automation

CamilleGuillory commented 7 months ago

Hi, As discussed here a flow example that (ideally) should trigger 4 exceptions:

image

<?xml version="1.0" encoding="UTF-8"?>
<Flow xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>60.0</apiVersion>
    <assignments>
        <name>Set_varR_Record</name>
        <label>Set varR_Record</label>
        <locationX>176</locationX>
        <locationY>647</locationY>
        <assignmentItems>
            <assignToReference>varR_Record.Id</assignToReference>
            <operator>Assign</operator>
            <value>
                <elementReference>$Record.Id</elementReference>
            </value>
        </assignmentItems>
        <connector>
            <targetReference>Update_coll_varR_Record</targetReference>
        </connector>
    </assignments>
    <environments>Default</environments>
    <interviewLabel>Contact After Save@ {!$Flow.CurrentDateTime}</interviewLabel>
    <label>Contact After Save@</label>
    <processMetadataValues>
        <name>BuilderType</name>
        <value>
            <stringValue>LightningFlowBuilder</stringValue>
        </value>
    </processMetadataValues>
    <processMetadataValues>
        <name>CanvasMode</name>
        <value>
            <stringValue>AUTO_LAYOUT_CANVAS</stringValue>
        </value>
    </processMetadataValues>
    <processMetadataValues>
        <name>OriginBuilderType</name>
        <value>
            <stringValue>LightningFlowBuilder</stringValue>
        </value>
    </processMetadataValues>
    <processType>AutoLaunchedFlow</processType>
    <recordUpdates>
        <name>Specify_condition_Previous_RecordId</name>
        <label>Specify condition Previous RecordId</label>
        <locationX>176</locationX>
        <locationY>539</locationY>
        <connector>
            <targetReference>Set_varR_Record</targetReference>
        </connector>
        <filterLogic>and</filterLogic>
        <filters>
            <field>Id</field>
            <operator>EqualTo</operator>
            <value>
                <elementReference>$Record__Prior.Id</elementReference>
            </value>
        </filters>
        <inputAssignments>
            <field>Description</field>
            <value>
                <stringValue>test</stringValue>
            </value>
        </inputAssignments>
        <object>Contact</object>
    </recordUpdates>
    <recordUpdates>
        <name>Specify_condition_RecordId</name>
        <label>Specify condition RecordId</label>
        <locationX>176</locationX>
        <locationY>431</locationY>
        <connector>
            <targetReference>Specify_condition_Previous_RecordId</targetReference>
        </connector>
        <filterLogic>and</filterLogic>
        <filters>
            <field>Id</field>
            <operator>EqualTo</operator>
            <value>
                <elementReference>$Record.Id</elementReference>
            </value>
        </filters>
        <inputAssignments>
            <field>Description</field>
            <value>
                <stringValue>test</stringValue>
            </value>
        </inputAssignments>
        <object>Contact</object>
    </recordUpdates>
    <recordUpdates>
        <name>Update_coll_varR_Record</name>
        <label>Update coll varR_Record</label>
        <locationX>176</locationX>
        <locationY>755</locationY>
        <inputReference>varR_Record</inputReference>
    </recordUpdates>
    <recordUpdates>
        <name>Update_record_that_triggered_the_flow</name>
        <label>Update record that triggered the flow</label>
        <locationX>176</locationX>
        <locationY>323</locationY>
        <connector>
            <targetReference>Specify_condition_RecordId</targetReference>
        </connector>
        <inputAssignments>
            <field>Description</field>
            <value>
                <stringValue>test</stringValue>
            </value>
        </inputAssignments>
        <inputReference>$Record</inputReference>
    </recordUpdates>
    <start>
        <locationX>50</locationX>
        <locationY>0</locationY>
        <connector>
            <targetReference>Update_record_that_triggered_the_flow</targetReference>
        </connector>
        <object>Contact</object>
        <recordTriggerType>CreateAndUpdate</recordTriggerType>
        <triggerType>RecordAfterSave</triggerType>
    </start>
    <status>Active</status>
    <variables>
        <name>varR_Record</name>
        <dataType>SObject</dataType>
        <isCollection>false</isCollection>
        <isInput>false</isInput>
        <isOutput>false</isOutput>
        <objectType>Contact</objectType>
    </variables>
</Flow>
alecdorner commented 7 months ago

Hi team, @CamilleGuillory reached out to me to collaborate on this initiative for the lightning flow scanner since I implemented it in the Flow Analyzer. I identified a number of ways the update could be accomplished, and covered those. Some of the scenarios are obviously a bit silly and hard to believe that someone would actually do it, but I included them for completeness. I've listed them below.

  1. The update records element updates the $Record variable
  2. The update records element uses filter criteria and filters to Id = $Record.Id
  3. The update records element uses another record variable of the object type to which the $Record.Id has been assigned as the Id using an assignment element
  4. The update records element uses filter criteria and filters to Id = a text variable to which $Record.Id has been assigned using an assignment element
  5. The update records element uses filter criteria and filters to Id = a text variable that has $Record.Id as it's default value

I think there are probably some other ways that it could be done, but that rabbit hole may be a bit too deep. For instance, if someone uses the $Record variable to query for the same record in a get records element and then updates that record. It feels ridiculous, but wouldn't be caught by my app.