1

Problem:

I had a Contact Support Form on a community that allows users to create a case for a customer. This form contains customer contact information (email, name, phone, etc). Currently, if there is not a contact match in our system, when the case is created, the Contact associated with the Community User is being linked to the Case by default.

Attempts:

  1. Override in Trigger: I tried basically nulling out the contact in the Case trigger based on certain criteria that would isolate this type of case. This did not work--no matter what context I set the contact to null at, the Contact for the case was eventually reassigned to the Community User (i.e. the cases creator).

  2. Override in Process Builder: Similar to my attempt in the trigger, I added logic in a process builder to isolate this specific type of case on creation, and attempted to null the contact assignment there, but the Contact of the Community User still ended up getting assigned.

  3. Custom Component: I created a custom lightning web component that creates a case. I originally thought this was working when I tested via the community builder. However, when I tested as a logged in community user the user contact still linked. I tried the following submission mechanisms:

    a. I submitted and created a case via lightning-record-edit-form's submit. This does not work.

    b. I linked my lightning web component to an apex class controller, and created and inserted the case via apex. This also does not work

Questions:

  1. Is it possible to prevent this default behavior of linking Community Users as the contact for cases they've created when there is no contact match?
  2. Are there ways to avoid this via apex?
  3. Is there some checkbox or setting I am missing?
  4. Is there a way to prevent this behavior directly on the Contact Support Form?
  5. Any other ideas of how to resolve this?

Also note: this is a Customer Community Plus User.

Thanks!

1

What you need to do is escape your context. A few years ago, you could do this by sending yourself an email message. Now you can do it more elegantly using a Platform Event and a Callable.

First define your platform event. Mine is called Async_Message__e and it has a paylod and a type.

Then define your AsyncActionUtil class. This will contain a way of inserting these platform events and handling them after insert. Your class should contain the insert method:

public static void insertAsyncMessage(String type, String payload) {
  Async_Message__e[] messages = new List<Async_Message__e>{
    new Async_Message__e(Type__c = type, Payload__c = payload)
  };
  // Call method to publish events
  Database.SaveResult[] results = EventBus.publish(messages);
}

The handle insert method (called from a trigger)

public static void handle(Async_Message__e[] messages) {
  for (Async_Message__e message : (Async_Message__e[]) Trigger.new) {
      handleAsyncMessage(message);
    }
  }

public static void handleAsyncMessage(Async_Message__e message) {
  try {
    switch on message.Type__c {
      when 'Callable' {
        Map<String, Object> callableParams = (Map<String, Object>) JSON.deserializeUntyped(
          message.Payload__c
        );
        String className = (String) callableParams.get('className');
        String methodName = (String) callableParams.get('methodName');
        String params = (String) callableParams.get('params');
        Map<String, Object> paramsMap = (Map<String, Object>) JSON.deserializeUntyped(params);
        //make the call!
        Callable someClass = (Callable) Type.forName(className).newInstance();
        someClass.call(methodName, paramsMap);
      }
    }
  } catch (Exception e) {
    System.debug('ASYNC EXCEPTION: ' + 'Line ' + e.getLineNumber() + ' ' + e.getMessage());
  }
}

"Callable" is just a string type that I add to the Async_Message__e on insert so that this object can be used for multiple porposes.

The Payload__c is just a JSON encoded string with whatever you want in it.

Your trigger looks like this:

trigger AsyncMessageTrigger on Async_Message__e(after insert) {
  AsyncActionUtil.handle(Trigger.new);
}

And the Callable class (implements the Callable interface) uses the AsyncActionUtil like this:

//completely bypasses any restrictions on the user running this.
AsyncActionUtil.enqueueCallable(
  'MyClass',
  'saveSObjects',
  new Map<String, Object>{'sObjects' => caseObjects}
);

In your case, your list would be cases that you want to be inserted... with no default contact.

The saveSObjects method is a not actually a method - it's a action that invokes something on the Callable class and looks like this:

// Dispatch actual methods
public Object call(String action, Map<String, Object> args) {
  switch on action {
    when 'saveSObjects' {
      try {
        Object[] objs = (Object[]) args.get('sObjects');
        SObject[] sobjs = (SObject[]) JSON.deserialize(JSON.serialize(objs), SObject[].class);
        Database.insert(sobjs, false);
      } catch (Exception e) {
        return null;
      }
    }
    when else {
      throw new YourCustomException('Callable Method not implemented');
    }
  }
  return null;
}

So in short:

  • Your originating class uses AsyncActionUtil to insert a Platform Event.
  • Your originating class is a Callable
  • The Platform Event is handled by AsyncActionUtil
  • AsyncActionUtil calls the action on the Callable class
  • Profit!!

PS, you can actually do that insert/update on the AsyncActionUtil class if you want but I did it this way so it would be more flexible (you can perform any action you like)

Hope this helps.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.