Salesforce.com触发器:将联系人潜在客户源复制到商机

时间:2012-12-05 02:04:44

标签: triggers salesforce apex-code soql

我坚持编写这个Salesforce.com触发器的语法(也许是逻辑)。我希望触发器检查是否在商机的ContactRoles中列出了主要联系人。如果列出了主要内容,我需要从相应的联系人中查找LeadSource,并将该值插入商机的主要来源。

非常感谢任何提示或提示!

trigger UpdateContactLeadSource on Opportunity (after insert, after update) {

//Declare the Lead Source Variable which will hold the contact's lead source
string leadsource;

// See if there is a primary contact listed on the Opportunity
for (Opportunity o : Trigger.new) {
OpportunityContactRole[] contactRoleArray =
   [select ContactID, isPrimary from OpportunityContactRole where OpportunityId = :o.id ORDER BY isPrimary DESC, createdDate];

    // If the there is a primary contact, then...
    if (contactRoleArray.size() > 0) {

    // Lookup ContactID on the Contacts table to find the lead source
    for (Contact contact : [SELECT LeadSource FROM Contact WHERE Contact.Id = :OpportunityContactRole.ContactId LIMIT 1]) 

    // Store the actual lead source in the leadsource variable
    { Contact.LeadSource = leadsource;}
    update Opportunity.LeadSource = leadsource; }
}
}

2 个答案:

答案 0 :(得分:1)

您的代码中有几个非常糟糕的东西。希望你不会觉得被冒犯,这是一个很好的要求和学习机会......

  1. after insert在这里没有任何意义。根据定义,如果您刚刚完成插入此商机,它将不会有任何联系人角色。*
  2. after update是好的。 before update会更好,因为您只需填写该值,您就可以免费保存到数据库。
  3. 您可能需要将此逻辑复制到OpportunityContactRole上的类似触发器吗?而不是复制代码 - 使用一些可以从两个地方调用的类?
  4. 我假设你只想在商机上的LeadSource为空时填写它?
  5. 你在循环中选择,这是一个非常糟糕的做法;)你的触发器不是“批量”,当有人在一个事务中更新10个机会时(例如通过使用Data Loader)它可能会失败,因为我们有一个限制触发器中最多20个查询。
  6. 您不需要ORDER BYLIMIT 1等 - Salesforce会保护您并且只允许1个联系人成为主要联系人。即使你想用Data Loader加载它们也是如此。
  7. 您似乎可以从阅读Salesforce中的关系如何工作中受益。与普通的SQL语言相比,“点符号”和子查询看起来有点奇怪,但您可以使用它们来大大简化代码。如果你在过去做了一些面向对象的编程,将会有所帮助。这是some guide,这里是official docs
  8. TL; DR

    trigger fillLeadSource on Opportunity (before update) {
        /*  I'm assuming you want to fill LeadSource in only if it was blank. 
            If that's not the case - just delete this first part of code and in the query instead of ":oppsToFill" bind to ":trigger.new" or
            ":trigger.newMap.keyset()".
            (I know they look weird but you can do it, bind objects/collections in queries that expect Ids / collections of Ids)
        */
        Set<Id> oppsToFill = new Set<Id>();
        for(Opportunity o : trigger.new){
            if(o.LeadSource == null) {
                oppsToFill.add(o.Id);
            }
        }
    
        // Now we'll select all possible contacts wasting only 1 query.
        if(!oppsToFill.isEmpty()){
            List<OpportunityContactRole> roles = [SELECT OpportunityId, Contact.Name, Contact.LeadSource
                FROM OpportunityContactRole
                WHERE isPrimary = true AND Contact.LeadSource != null AND OpportunityId IN :oppsToFill];
    
            if(!roles.isEmpty()){
                for(OpportunityContactRole ocr : roles){
                    Opportunity oppToBeFilled = trigger.newMap.get(ocr.OpportunityId);
                    System.debug('Changing lead source on ' + oppToBeFilled.Name + ' from ' + oppToBeFilled.LeadSource + ' to ' + ocr.Contact.LeadSource + ' (thx to ' + ocr.Contact.Name + ' being the Primary Contact).');
                    oppToBeFilled.LeadSource = ocr.Contact.LeadSource;
                }
            }
        }
        // That's it. If there was a primary contact with Lead source, data will be copied over.
        // If there was no primary contact or he didn't have the source filled in - tough luck, we did our best.
        // Since it's before update, you get save to database for free.
    }
    

    • 精细打印到#1,用于高级场景;)如果你有另一个插入后触发器将它们添加到某个地方,你可以在保存后立即拥有Contact Roles ......但这样会非常混乱(取决于哪个)触发器首先触发我会说你会得到一个非常有趣的随机行为,好运调试)。如果在给定对象上有多个具有相同触发关键字的触发器,则建议只有1个触发器并完全控制动作顺序。有关详情,请参阅http://www.embracingthecloud.com/2010/07/08/ASimpleTriggerTemplateForSalesforce.aspx

    编辑回复评论#3

    中的问题

    在新触发器中你需要类似但不相同的代码(在这种情况下,无论是before还是after都无关紧要 - 我们需要明确更新机会)。这里有点糟糕,因为您想要查看的字段不能直接使用 - 您可以访问OpportunityId,ContactId但不能访问Contact.LeadSource。这样的事情可以解决问题:

    trigger ContactRoleRollup on OpportunityContactRole(after insert, after update){
        Map<Id,Id> oppToContactMap = new Map<Id, Id>();
        for(OpportunityContactRole ocr : trigger.new){
            if(ocr.isPrimary){
                oppToContactMap.put(ocr.OpportunityId, ocr.ContactId);
            }
        }
        if(!oppToContactMap.isEmpty()){
            List<Opportunity> oppsToUpdate = [SELECT Id FROM Opportunity WHERE LeadSource = null AND Id IN :oppToContactMap.keyset()];
            Map<Id, Contact> contacts = [SELECT Id, LeadSource FROM Contact WHERE LeadSource != null AND Id IN :oppToContactMap.values()];
    
            for(Opportunity o : oppsToUpdate){
                Id contactId = oppToContactMap.get(o.Id);
                Contact c = contacts.get(contactId);
                if(c != null){
                    o.LeadSource = c.LeadSource;
                }
            }
            update oppsToUpdate;
        }
    }
    

    这里很有趣,因为这次更新将触发我们对机会的旧触发器。如果你离开我的“跳过,如果填充了leadSource”,那应该没问题,但你仍然想要探索两件事:

    • 使用一些helper static flag in a class,将其称为“skipTriggerOnOpps”,您将在OpportunityContactRoles的触发器中设置此标志,并在其中包含Opportunity触发器的整个代码,以便在我们已经处理好铅的情况下不会执行源同步。
    • 从理论上讲,你可以在不改变任何东西的情况下“触摸”机会(将旧触发器视为一种好处,在这种情况下不会产生不必要的副作用)。对我而言,它看起来有点太神奇,但如果它评论了这里发生了什么,它可能会导致更少的代码,更少的逻辑重复,更少的单元测试......只要after触发它就会工作我们刚刚修改过的联系人角色查询会看到新值。它必须看起来像那样

      trigger ContactRoleRollup on OpportunityContactRole(after insert, after update){
          Set<Id> oppIds = new Set<Id>();
          for(OpportunityContactRole ocr : trigger.new){
              if(ocr.isPrimary){
                  oppIds.add(ocr.OpportunityId);
              }
          }
          if(!oppIds.isEmpty()){
              update [SELECT Id FROM Opportunity WHERE LeadSource = null AND Id IN :oppIds];
              // That's it. Call update directly on returned list without changing anything, let the other trigger worry about the logic
          }
      }
      

答案 1 :(得分:0)

eyecream的机会触发器是最有帮助的并且正在为我解决问题。值得指出的是,不幸的是,SFDC尚未支持OpportunityContactRole触发他的假设。正如这个想法(https://success.salesforce.com/ideaview?id=08730000000BrdvAAC)所指出的那样,目前无法在OCR上触发。