function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
KevinG514KevinG514 

Unable to save custom attributes to interaction log in service console


I'm not sure when this functionality broke, the last time that I tested and observed that it was working was at Dreamforce. When running our CTI integration (that we wrote) in the service cloud console, we can no longer save custom call log attributes. If we switch to the normal HTML view, things work fine. 

Looking into the code, i see that in OnCallEnd, there is this if statement 

if (pLog->GetIsCtiWidgetMode()) { 
pLog->SetAttribute(KEY_SAVE_CALL_LOG, pLog->GetSaveOnEnd()); 
} else { 
GetAppExchange()->UpsertCallLog(pLog); 


so the KEY_SAVE_CALL_LOG attribute is correctly getting set on the next ui update 

What i assume is supposed to be happening is that KEY_SAVE_CALL_LOG gets set to true and then there is a UI message to the CTI adapter to save what is in memory for the call log 

(From UIHandleMessage) 
} else if (message==KEY_SAVE_CALL_LOG) { 
UISaveCallLog(parameters); 


But that message never is sent. I tried to hack it to force a save on the call end, but then we got double tasks created for the call.
Best Answer chosen by Admin (Salesforce Developers) 
KevinG514KevinG514

That worked pretty well for OnCallEnd, but if the user then add new notes then the custom fields that got mapped by the CTI are blown away because the interaction log didn't know about the updated fields.  Here is my updated trigger to take care of the before update case to make sure the fields didn't get wiped clear

 

trigger upsertOnCallObjectId on Task (before insert, after insert, before update) {
    Task[] preExistingTask = null; // A pre existing call log based on the callobject field
    if (Trigger.isBefore) {
        if(Trigger.isInsert){
            for (Task t : Trigger.new) {
                preExistingTask = [SELECT id, ActivityDate, subject, whoid, whatid, callType, type, callDisposition, description, Workgroup__c FROM Task WHERE callobject = :t.callobject];
                if (preExistingTask.size() > 0) {
                    t.callDisposition = preExistingTask[0].callDisposition;
                    t.subject = preExistingTask[0].subject;
                    t.whoid = preExistingTask[0].whoid;
                    t.whatid = preExistingTask[0].whatid;   
                    t.description = preExistingTask[0].description;
                    t.ActivityDate = preExistingTask[0].ActivityDate; 
   
                    if(preExistingTask[0].callType != ''){
                        t.callType = preExistingTask[0].callType;
                    }
                    
                    if(preExistingTask[0].type!= ''){
                        t.type= preExistingTask[0].type;
                    }
                    
                    if(preExistingTask[0].Workgroup__c!= ''){
                        t.Workgroup__c = preExistingTask[0].Workgroup__c;             
                    }
                }
            }
        }      
        else if(Trigger.isUpdate)
        {
            //make sure these fields don't get cleared out
            for (Task t : Trigger.new) {
                preExistingTask = [SELECT id, ActivityDate, subject, whoid, whatid, callType, type, callDisposition, description, Workgroup__c FROM Task WHERE callobject = :t.callobject];
                if (preExistingTask.size() > 0) {
                    
                    if(t.callType == ''){
                        t.callType = preExistingTask[0].callType;
                    }
                    
                    if(t.type == ''){
                        t.type= preExistingTask[0].type;
                    }
                    
                    if(t.Workgroup__c == ''){
                        t.Workgroup__c = preExistingTask[0].Workgroup__c;             
                    }
                }
            }
        } 
    } else if (Trigger.isAfter) {
        for (Task t : Trigger.new) {
            preExistingTask = [SELECT id, subject, lastModifiedDate FROM Task WHERE callobject = :t.callobject ORDER BY lastModifiedDate ASC];   
            if (preExistingTask.size() == 2) {
                delete preExistingTask[0];
            }
        }
    }    
}

All Answers

sfdcAnonsfdcAnon

Unfortunately this is an impact of the use of the interaction log. Saving custom fields to a task in the Console via the adapter is not available out of the box. It requires some customization.

 

There are 2 ways to resolve this

- Via a salesforce trigger on tasks and a modification to the toolkit (code below)

- Using a custom interaction log (see http://blogs.salesforce.com/product/2012/02/custominteractionlog.html)

 

Both options are farily straight forward. Going with the trigger option requires less coding but also an adapter modification as described below. The visualforce option gives full control and customization ability to the customer. It is the preferred method due to this when there is a VF skillset on the customer side.

 

This is a challenge multiple partners have run into. With the new Cloud CTI API available in June with Summer '12, you will be able to embed your own custom web softphone directly into salesforce, and save logs to any objects with any fields specified via a JavaScript map. No more client, no more toolkit, no more disconnect. Also, the same API will work consistently across the standard salesforce app and Service Cloud Console.

 

Please update if more input is needed.

 

Trigger Code:

First - Remove the code block  if (pLog->GetIsCtiWidgetMode()) . The GetAppExchange()->UpsertCallLog(pLog);  should always be called.

Then add the code below as a Trigger on tasks in the salesforce org.

<pre>

trigger upsertOnCallObjectId on Task (before insert, after insert) {

    if (Trigger.isBefore) {

        Task[] preExistingTask = null; // A pre existing call log based on the callobject field

        for (Task t : Trigger.new) {

            preExistingTask = [SELECT uuid__c, id, ActivityDate, subject, whoid, whatid, callType, callDisposition, description FROM Task WHERE callobject = :t.callobject];

            if (preExistingTask.size() > 0) {

                if (t.uuid__c == null ) {

                    t.uuid__c = preExistingTask[0].uuid__c;

                } else {

                    t.callDisposition = preExistingTask[0].callDisposition;

                    t.subject = preExistingTask[0].subject;

                    t.whoid = preExistingTask[0].whoid;

                    t.whatid = preExistingTask[0].whatid;                   

                    t.callType = preExistingTask[0].callType;

                    t.description = preExistingTask[0].description;

                    t.ActivityDate = preExistingTask[0].ActivityDate;

                }               

            }

        }       

    } else if (Trigger.isAfter) {

        Task[] preExistingTask = null; // A pre existing call log based on the callobject field

        for (Task t : Trigger.new) {

            preExistingTask = [SELECT id, subject, lastModifiedDate FROM Task WHERE callobject = :t.callobject ORDER BY lastModifiedDate ASC];   

            if (preExistingTask.size() == 2) {

                delete preExistingTask[0];

            }

        }

    }    

}

</pre>

sfdcAnonsfdcAnon

Note that in the task trigger, you would need to add the custom fields ou are saving.

KevinG514KevinG514

That worked pretty well for OnCallEnd, but if the user then add new notes then the custom fields that got mapped by the CTI are blown away because the interaction log didn't know about the updated fields.  Here is my updated trigger to take care of the before update case to make sure the fields didn't get wiped clear

 

trigger upsertOnCallObjectId on Task (before insert, after insert, before update) {
    Task[] preExistingTask = null; // A pre existing call log based on the callobject field
    if (Trigger.isBefore) {
        if(Trigger.isInsert){
            for (Task t : Trigger.new) {
                preExistingTask = [SELECT id, ActivityDate, subject, whoid, whatid, callType, type, callDisposition, description, Workgroup__c FROM Task WHERE callobject = :t.callobject];
                if (preExistingTask.size() > 0) {
                    t.callDisposition = preExistingTask[0].callDisposition;
                    t.subject = preExistingTask[0].subject;
                    t.whoid = preExistingTask[0].whoid;
                    t.whatid = preExistingTask[0].whatid;   
                    t.description = preExistingTask[0].description;
                    t.ActivityDate = preExistingTask[0].ActivityDate; 
   
                    if(preExistingTask[0].callType != ''){
                        t.callType = preExistingTask[0].callType;
                    }
                    
                    if(preExistingTask[0].type!= ''){
                        t.type= preExistingTask[0].type;
                    }
                    
                    if(preExistingTask[0].Workgroup__c!= ''){
                        t.Workgroup__c = preExistingTask[0].Workgroup__c;             
                    }
                }
            }
        }      
        else if(Trigger.isUpdate)
        {
            //make sure these fields don't get cleared out
            for (Task t : Trigger.new) {
                preExistingTask = [SELECT id, ActivityDate, subject, whoid, whatid, callType, type, callDisposition, description, Workgroup__c FROM Task WHERE callobject = :t.callobject];
                if (preExistingTask.size() > 0) {
                    
                    if(t.callType == ''){
                        t.callType = preExistingTask[0].callType;
                    }
                    
                    if(t.type == ''){
                        t.type= preExistingTask[0].type;
                    }
                    
                    if(t.Workgroup__c == ''){
                        t.Workgroup__c = preExistingTask[0].Workgroup__c;             
                    }
                }
            }
        } 
    } else if (Trigger.isAfter) {
        for (Task t : Trigger.new) {
            preExistingTask = [SELECT id, subject, lastModifiedDate FROM Task WHERE callobject = :t.callobject ORDER BY lastModifiedDate ASC];   
            if (preExistingTask.size() == 2) {
                delete preExistingTask[0];
            }
        }
    }    
}

This was selected as the best answer
sfdcAnonsfdcAnon

Glad you were able to work through this.