BigObjects and Triggers

Some months ago I wrote an entry about BigObjects and since that moment I go lot of comments with questions and some useful information that I missed in my post and I would like to include here because sometimes, we do not read comments.

Not all SOQL statements are valid. For now, the !=, LIKE, NOT IN, EXCLUDES, and INCLUDES operators are not valid in any query.

There are already some Standard Big Objects like FieldHistoryArchive, so do not try to create one directly, and check before if it already exist.

Unfortunately SOSL is not supported by BigObjects.

But maybe the most important one was the fact that people was not able to create BigObject records from Triggers. It was a big surprise for me because documentation said that you could not create triggers related to a Big Object, but nothing about not be able to create Big Object records from triggers. So today I decided to give it a try.

If you have taken a look at my blog, the use case was to archive Code Review records so that we could end up with Code Review History records. Although it doesn’t make too much sense, create History records in a trigger, I decided to make it simple and continue with these objects.

So the first idea was to create a Trigger related to CodeReview__c:

trigger CodeReviewTrigger on CodeReview__c (after insert)
{
   List<CodeReviewHistoric__b> crhToInsert = new List<CodeReviewHistoric__b>(); 
   for (CodeReview__c cr: Trigger.new)    
   { 
      CodeReviewHistoric__b crh = new CodeReviewHistoric__b();        
      crh.CanBeMerged__c = cr.CanBeMerged__c ? 'true' : 'false';        
      crh.CodeReviewDate__c = cr.CodeReviewDate__c;         
      crh.Comments__c = cr.Comments__c;        
      crh.Employee__c = cr.Employee__c;        
      crh.Score__c = cr.Score__c;        
      crh.Story__c = cr.Story__c;        
      crhToInsert.add(crh);    
   }
   Database.insertImmediate(crhToInsert);
}

However, when I tried to create a Code Review record via the UI, I got below error

Captura de pantalla 2018-02-18 a las 15.16.26

and if I checked log, I also got this one.

Captura de pantalla 2018-02-18 a las 15.16.54

So came back to BigObjects product manager suggestion:

However a workaround could be to put the insertImmediate() call into a queueable method

But why queuable method? I think the key point is that we need a different context, a new transaction, so it doesn’t matter the way to get it, and I decided to keep things simple using a @future method in order to perform the Database.insertInmediate action. Then, this method will be called from the trigger.

But this annotation has several restrictions, like argument types. It doesn’t support a list of SObjects, but it allows a List of Ids.

So Trigger will look like this:

trigger CodeReviewTrigger on CodeReview__c (after insert)
{    
   List<Id> codeReviewIds = new List<Id>(); 
   for (CodeReview__c cr: Trigger.new)    
   {        
      codeReviewIds.add(cr.Id);    
   }
   AsyncProcess.createBigObjectRecords(codeReviewIds);
}

And @future one like this:

public with sharing class AsyncProcess
{
   @future
   public static void createBigObjectRecords(List<Id> crIds)
   {
      List<CodeReviewHistory__b> crhToInsert = new List<CodeReviewHistory__b>();
      for (CodeReview__c cr: [Select Id, 
                                     CanBeMerged__c, 
                                     CodeReviewDate__c, 
                                     Comments__c, 
                                     Employee__c, 
                                     Score__c, 
                                     Story__c 
                               From CodeReview__c 
                               Where Id In :crIds])    
      {
         CodeReviewHistory__b crh = new CodeReviewHistory__b();
         crh.CanBeMerged__c = cr.CanBeMerged__c ? 'true' : 'false';            
         crh.CodeReviewDate__c = cr.CodeReviewDate__c;             
         crh.Comments__c = cr.Comments__c;            
         crh.Employee__c = cr.Employee__c;            
         crh.Score__c = cr.Score__c;            
         crh.Story__c = cr.Story__c;            
         crhToInsert.add(crh);    
      }     
      Database.insertImmediate(crhToInsert);    
   }
}

And now it works sucessfuly

Captura de pantalla 2018-02-18 a las 16.37.46

Captura de pantalla 2018-02-18 a las 16.39.40.png

Any other options? Batch Apex, Queueable, even, you can use Async SOQL via a http call.

I’m sure that I still miss things about BigObjects but another amazing resource to check is the Salesforce MVP Alba A. Rivas blogs about this technology.

 

How to attach a file to Opportunity Lines

Some days ago, I received an email with a simple question. “How can I get some attachments related to my object?”

I know what you are thinking. The answer is easy. Actually we have 2 options, and both with #clicksnotcode:

  1. Add Attachment and Notes related list to your object.
  2. Enable Chatter on our object.

But her question didn’t end there. Actually she needed these files attached to a certain Standard object, Opportunity Product or Opportunity Line Item one. Ok, in that case above options do not work because we find some restrictions on this object.

Having this information, the only solution that came to my mind was to have a custom object as container where we can have the list of Attachments that we need to link to Opportunity Line.

  • New Custom Object – Attachment Container

Bellow example shows the easiest object that you can create with only Standard fields, and no custom field at all. But you can customize as much as you want.

  • Add Note and Attachments: 

Double check that Note and Attachments related list is in the page layout, so after creating a record, you can attach a new file.

  • New lookup field:

Now we would need to create a lookup field on Opportunity Line Item Standard object, so that, we can link each record to a single Attachment Container record, and its list of files.

Captura de pantalla 2015-06-06 a las 23.22.54

Having these objects structure, we would be able to use Apex code an access to all files like they were attached to Opportunity Lines directly.

We can only find a single issue. How can we avoid that the same Attachment Container record could be related to a single Opportunity record?

  • Create an Opportunity Line trigger:

Via a trigger, we could make sure that 2 Opportunity Lines will not try to use the same Attachment Container record, and be sure that files would be ONLY related to a single Opportunity Line record. In this way we can enforce a relationship of 1 to1 using lookup fields.

I would also like to raise the fact that we are doing validations on after actions. Why? What about if the end user decide to create its own trigger and modify the record during the before? Having our validations at the end, we will be sure that we would check final values. Please, find more information about validations on triggers on this blog entry.

trigger OpportunityLineTrigger on OpportunityLineItem (after insert, 
                                                       after update)
{
    //Retrieve attachments that already exist
    Map<Id, Id> currentAttachmentAndOppLines = new Map<Id, Id>();
    for(OpportunityLineItem currentOppLine : [Select Id, Attachment_Container__c
                                              From OpportunityLineItem])
    {
         currentAttachmentAndOppLines.put(currentOppLine.Attachment_Container__c,
                                          currentOppLine.Id);
    }

    if(Trigger.isAfter)
    {
        for(OpportunityLineItem oppLine : Trigger.new)
        {
            Id attachmentContainerId = oppLine.Attachment_Container__c;
            if(currentAttachmentAndOppLines.containsKey(attachmentContainerId))
            {
                if(Trigger.isUpdate)
                {
                    Id oppLineId = oppLine.Id;
                    if(currentAttachmentAndOppLines.get(attachmentContainerId) != oppLineId)
                    {
                        oppLine.addError('This Attachment Container is already in used.');
                    }
                }
            }
        }
     }
}

Do you have any other idea? Don’t be shy and share. I’m looking forward to knowing it !!