本文已参与「新人创作礼」活动,一起开启掘金创作之路。
场景描述
在使用Sales Cloud报价功能时,经常会有报价产品与业务机会产品同步的需求,但标准同步功能存在一些局限性,比如只能同步标准字段,至于是不是可以同步所有标准字段并不清楚,再比如一旦记录锁定后,非系统管理员用户不能操作同步与停止同步按钮等等。
在阅读此篇blog前,有必要知道报价的基础知识,如报价字段、报价的同步方式、报价同步故障排除。\
问题与方案
问题1:如何解决报价与业务机会自定义字段同步问题?
A:安装AppExchange中的package之后,进入Custom Seting管理同步字段映射关系即可。Custom Quote Sync在AppExchange上有Managed和Unmanaged两个版本,相关的安装步骤和异常详见Custom Quote Sync Setup Guide。
问题2:非管理员用户在记录锁定后,如何实现自动同步和关闭?
思路:需要使用Trigger和@future异步方式解决,若记录锁定,则需要使用webservice技术。
Code Sample:下面是解决报价审批通过后,自动同步和停止同步报价与业务机会
public class ACC_QuoteTriggerHandler extends ACC_TriggerHandler {
public static boolean isExecuting = false;// Prevent recursion
public override void afterUpdate() {
if(ACC_QuoteTriggerFunction.isExecuting) {
return;
}
ACC_QuoteTriggerHandler.isExecuting = true;
Map<Id, Quote> quoteMap = new Map<Id, Quote>([SELECT OpportunityId, Opportunity.StageName FROM Quote WHERE Id IN :Trigger.New AND RecordTypeId = :rtMap.get('ACC_PAPAGZ') AND Status = :Label.ACC_GZ_Quote_Approved_in_Salesforce]);
Map<String, String> quoteId2oppIdMap = new Map<String, String>();
if(quoteMap.size() > 0) {
for(String str : quoteMap.keySet()) {
if(quoteId2oppIdMap.keySet().contains(str)) {
continue;
}else {
quoteId2oppIdMap.put(str, quoteMap.get(str).OpportunityId);
}
}
System.debug('quoteId2oppIdMap: ' + quoteId2oppIdMap);
}
if(quoteId2oppIdMap.size() > 0) {
System.debug('start to syncAndUnsyncQuote...');
if(!System.isFuture() && !System.isBatch()) {// very important
ACC_QuoteOnClickJsHandler.syncAndUnsyncQuote(quoteId2oppIdMap);
}
}
ACC_QuoteTriggerHandler.isExecuting = false;
}
}
global class ACC_QuoteOnClickJsHandler {
/**
Function Description: sync and unsync Quote called by apex trigger.
Parameters: Map<String, String> quoteId2oppIdMap
Return Value: void
Author: Wilson Xu
Date: 2018-1-15
**/
@future (callout=true)
public static void syncAndUnsyncQuote(Map<String, String> quoteId2oppIdMap) {
List<Opportunity> oppList = new List<Opportunity>();
for(String str : quoteId2oppIdMap.keySet()) {
Opportunity opp = new Opportunity();
opp.Id = quoteId2oppIdMap.get(str);
opp.SyncedQuoteId = str;
oppList.add(opp);
}
System.debug('oppList: ' + oppList);
try{
if(oppList.size() > 0) {
Database.SaveResult[] srList = Database.update(oppList, false);
List<Opportunity> successOppList = new List<Opportunity>();
for(Database.SaveResult sr : srList) {
if (sr.isSuccess()) {
Opportunity successOpp = new Opportunity();
successOpp.Id = sr.getId();
successOpp.SyncedQuoteId = null;
successOppList.add(successOpp);
}else {
System.debug('Updating returned the following errors.');
for(Database.Error e : sr.getErrors()) {
System.debug(e.getMessage());
}
}
}
System.debug('successOppList: ' + successOppList);
if(successOppList.size() > 0) {
update successOppList;
}
}
}catch(Exception e) {
System.debug('syncAndUnsyncQuote failed: ' + e.getMessage());
}
}
}
复制代码
以上不难看出,同步与否的核心为Opportunity的SyncedQuoteId字段,为null,则停止同步,当不为null时,相关的Opportunity的IsSyncing将被自动勾上。\