+ Start a Discussion
CatieCatie 

Chatter Trigger - Opportunity Closed Won

Hi, 

I am looking to send trigger a chatter alert in a group when an opportunity is changed to Closed Won. We are currently using email alerts (AND( ISCHANGED(StageName), Probability = 1, NOT(Owner.Id = "005d0000001TUC8")) but would like to move the interaction to chatter: 

 

Below is my attempt but

 

1. When I try this I get the error - Compile Error: unexpected token: insert at line 26 column 8 

2.As it is in the workflow - I would like to have one person's opportunitiy's (NOT(Owner.Id = 005d0000001TUC8) not trigger this workflow for privacy reasons

 

Could anyone help me clean this up? Thank you so much!

 

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

String status;
String OppAccName;
//Decimal OppAmount;
FeedItem post = new FeedItem();
    
    for(Opportunity o : Trigger.new) {
        if(Trigger.isInsert) {
                               if(o.IsWon == true && Trigger.oldMap.get(o.id).IsWon == false) {
                    for (Opportunity oppty : [SELECT Account.Name FROM Opportunity WHERE Id =:o.Id] ) {
                        OppAccName = oppty.Account.Name;
                    }
                   o.owner +' just won' + OppAccName + 'for' o.expectedrevenue + ‘!';
                    }
                    else {
                        return;
                    }
                }
            
            post.ParentId = //0F9K000000005LR
            post.Title = o.Name;
            post.Body = status;
            }
        }
        insert post;
    }
}

 

Best Answer chosen by Admin (Salesforce Developers) 
spatelcespatelce

Hi Catie,

 

First...no need to apologize. None of us are born genius and we were also at begineer stage. Please feel free to post as many questions/doubts you get.

 

Good news...You can directly have opportunity with 'Closed Win'. I skipped single step and mention that it is not possible. Now, I have posted this code from collecting previous queries and my understanding. Just make sure before you use this code. Please uncheck box 'Is Active' for any other implemeted Trigger on Opportunity update for same purpose.

 

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

String status;
String OppAccName;
String OppOwnerName;
FeedItem post = new FeedItem();
    
    for(Opportunity o : Trigger.new) {
        if(o.OwnerId == '005d0000001TUC8') { //It will not post record for for this user to group.
            return;
        }
        else {
            if(Trigger.isInsert ) { 
                if( o.Amount >= 1000.00 && o.IsWon == true ) { //This will be executed on new record insertion with amount >= 1,000.00
                    for (Opportunity oppty : [SELECT Account.Name, Owner.Name FROM Opportunity WHERE Id =:o.Id] ) {
                        OppAccName = oppty.Account.Name;
                        OppOwnerName = oppty.Owner.Name;
                    }    
                    status = OppOwnerName + ' just won ' + OppAccName + ' for ' + o.expectedrevenue + '!';

                    post.ParentId = '0F9K000000005LR';
                    post.Title = o.Name;
                    post.Body = status;
                    
                    insert post;
                }
            }    
            else {
                if ( Trigger.isUpdate ) {
                    if( o.Amount >= 1000.00 && o.IsWon == true && Trigger.oldMap.get(o.id).IsWon == false) { //This will be executed on update to existing record with amount >= 1,000.00
                        for (Opportunity oppty : [SELECT Account.Name, Owner.Name FROM Opportunity WHERE Id =:o.Id] ) {
                            OppAccName = oppty.Account.Name;
                            OppOwnerName = oppty.Owner.Name;
                        }    
                        status = OppOwnerName + ' just won ' + OppAccName + ' for ' + o.expectedrevenue + '!';
                            
                        post.ParentId = '0F9K000000005LR';
                        post.Title = o.Name;
                        post.Body = status;
                        
                        insert post;      
                    }
                }
            }
        }
    }    
}

 

Please feel free to post as many questions/queries you get over this. Hope this will serve your purpose.

 

-Saw

All Answers

spatelcespatelce
trigger ChatterWonOpportunity on Opportunity (after insert, after update) {

String status;
String OppAccName;
FeedItem post = new FeedItem();
    
    for(Opportunity o : Trigger.new) {
if(o.OwnerId == '005d0000001TUC8') {
return;
}
else
{ if(Trigger.isInsert) { if(o.IsWon == true && Trigger.oldMap.get(o.id).IsWon == false) { for (Opportunity oppty : [SELECT Account.Name FROM Opportunity WHERE Id =:o.Id] ) { OppAccName = oppty.Account.Name; } o.owner +' just won' + OppAccName + 'for' o.expectedrevenue + ‘!'; } else { return; } } post.ParentId = '0F9K000000005LR'; post.Title = o.Name; post.Body = status; } } insert post;
} } }

 

Possible explaination:

 

(1) Compile Error - You cannot directly have Id, it should be in single quote.

(2) At beginning of trigger execution, you can check for Opp Ownder Id. Post everything except perticular Id. You may also modify condition that I set to Not Equal to and continue your code, and set return in Else.

 

Hope this will help.

 

-Saw

CatieCatie

Would it be possible for you show a fix?

Thanks!

spatelcespatelce

Sure, but would you please elaborate more on how can I show a fix?

 

On my understanding, I have already posted a corrected code on my reply.

 

-Saw

CatieCatie

Thanks for your help. When I parse in your code I get:

Compile Error: line breaks not allowed in string literals at line 18 column -1

spatelcespatelce

Here you go. I have just tested this code in my instance and it is working without error.

 

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

String status;
String OppAccName;
FeedItem post = new FeedItem();
    
    for(Opportunity o : Trigger.new) {
        if(o.OwnerId == '005d0000001TUC8') {
            return;
        }
        else {
            if(Trigger.isInsert || Trigger.isUpdate ) {
                if(o.IsWon == true && Trigger.oldMap.get(o.id).IsWon == false) {
                    for (Opportunity oppty : [SELECT Account.Name FROM Opportunity WHERE Id =:o.Id] ) {
                        OppAccName = oppty.Account.Name;
                    }
                    status = o.Owner + ' just won ' + OppAccName + ' for ' + o.expectedrevenue + '!';
                }
                else {
                    return;
                }
            }
            
            post.ParentId = '0F9K000000005LR';
            post.Title = o.Name;
            post.Body = status;
        }
    }
    insert post;
}

 

Hope this will work.

 

-Saw

 

Please If this post is helpful, click on Star Button for Kudos. If this post solves your problem kindly mark it as solution.

CatieCatie

Thank you so much for your help- I'm almost there. You have been incredibly helpful.  Three last questions:

1. o.Owner is showing up as "null" in the chatter post - what is the correct way to put that?

2. If I wanted to only have it triggered by deals greater or equal to $1000.00-> would I tweak ->

if(o.IsWon == true && Trigger.oldMap.get(o.id).IsWon == false)

to 

if(o.IsWon == true &o.expectedrevenue≥1,000 && Trigger.oldMap.get(o.id).IsWon == false)



3. If I wanted to do the inverse of this and create a trigger for the person I am excluding from the first workflow(005d0000001TUC8) and trigger another workflow for them to just be sent to another specific group.

 

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

String status;
String OppAccName;
FeedItem post = new FeedItem();
    
    for(Opportunity o : Trigger.new) {
                 if(Trigger.isInsert || Trigger.isUpdate ) {
                if(o.IsWon == true & o.OwnerId == '005d0000001TUC8&& Trigger.oldMap.get(o.id).IsWon == false) {
                    for (Opportunity oppty : [SELECT Account.Name FROM Opportunity WHERE Id =:o.Id] ) {
                        OppAccName = oppty.Account.Name;
                    }
                    status = o.Owner + ' just won ' + OppAccName + ' for ' + o.expectedrevenue + '!';
                }
                else {
                    return;
                }
            }
            
            post.ParentId = '0F9K000000005LR';
            post.Title = o.Name;
            post.Body = status;
        }
    }
    insert post;

 

 

 

spatelcespatelce

You are welcome. I am glad that I was able to answer your questions.

 

Here is a series of answers:

 

(1) To display Opportunity Owner

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

String status;
String OppAccName;
String OppOwnerName;
FeedItem post = new FeedItem();
    
    for(Opportunity o : Trigger.new) {
        if(o.OwnerId == '005d0000001TUC8') {
            return;
        }
        else {
            if(Trigger.isInsert || Trigger.isUpdate ) {
                if(o.IsWon == true && Trigger.oldMap.get(o.id).IsWon == false) {
                    for (Opportunity oppty : [SELECT Account.Name, Owner.Name FROM Opportunity WHERE Id =:o.Id] ) {
                        OppAccName = oppty.Account.Name;
                        OppOwnerName = oppty.Owner.Name;
                    }

                    status = OppOwnerName + ' just won ' + OppAccName + ' for ' + o.expectedrevenue + '!';
                }
                else {
                    return;
                }
            }
            
            post.ParentId = '0F9K000000005LR';
            post.Title = o.Name;
            post.Body = status;
        }
    }    
    insert post;
}

 

(2) Deals greater then $1,000.00

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

String status;
String OppAccName;
String OppOwnerName;
FeedItem post = new FeedItem();
    
    for(Opportunity o : Trigger.new) {
        if(o.OwnerId == '005d0000001TUC8') {
            return;
        }
        else {
            if(Trigger.isInsert || Trigger.isUpdate ) {
                if(o.IsWon == true && o.Amount >= 1000.00 && Trigger.oldMap.get(o.id).IsWon == false) {
                    for (Opportunity oppty : [SELECT Account.Name, Owner.Name FROM Opportunity WHERE Id =:o.Id] ) {
                        OppAccName = oppty.Account.Name;
                        OppOwnerName = oppty.Owner.Name;
                    }

                    status = OppOwnerName + ' just won ' + OppAccName + ' for ' + o.expectedrevenue + '!';
                }
                else {
                    return;
                }
            }
            
            post.ParentId = '0F9K000000005LR';
            post.Title = o.Name;
            post.Body = status;
        }
    }    
    insert post;
}

 

(3) While only '005d0000001TUC8' will perform close deal action.

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

String status;
String OppAccName;
String OppOwnerName;
FeedItem post = new FeedItem();
    
    for(Opportunity o : Trigger.new) {
        if(o.OwnerId == '005d0000001TUC8') {
            if(Trigger.isInsert || Trigger.isUpdate ) {
                if(o.IsWon == true &&Trigger.oldMap.get(o.id).IsWon == false) {
                    for (Opportunity oppty : [SELECT Account.Name, Owner.Name FROM Opportunity WHERE Id =:o.Id] ) {
                        OppAccName = oppty.Account.Name;
                        OppOwnerName = oppty.Owner.Name;
                    }

                    status = OppOwnerName + ' just won ' + OppAccName + ' for ' + o.expectedrevenue + '!';
                }
                else {
                    return;
                }
            }
            
            post.ParentId = '0F9K000000005LR';
            post.Title = o.Name;
            post.Body = status;
        }
        else {
            return;
        }
    }        
    insert post;
}

 

Hope this helps...

 

-Saw

 

 

CatieCatie

Thank you so much! This seems to work unless someone creates and immediatly closes an opportunity: If you create an opportunity and immediatly set it to closed won - you get the error:

Apex trigger ChatterWonOpportunity caused an unexpected exception, contact your administrator: ChatterWonOpportunityHolly: execution of AfterInsert caused by: System.NullPointerException: Attempt to de-reference a null object: Trigger.ChatterWonOpportunityHolly: line 11, column 1
 Line 11:         if(o.IsWon == true &&Trigger.oldMap.get(o.id).IsWon == false) {
spatelcespatelce

That's right. You are getting an error because it returns no rows on execution. I tried to modify a code and also had some search for practicing but I got negative response for that.

 

I strongly suggest for not to chanage a code to go for direct 'Closed Win' stage but take two steps method. First create a record and on second moment, you may update it to 'Closed Win'.

 

Hope this helps...

 

-Saw

spatelcespatelce

Hey there,

 

here is one more update, and I promise this will not give you any further error.

 

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

String status;
String OppAccName;
String OppOwnerName;
FeedItem post = new FeedItem();
    
    for(Opportunity o : Trigger.new) {
        if(o.OwnerId == '005d0000001TUC8') {
            return;
        }
        else {
            if(Trigger.isInsert ) {
                if(o.StageName == 'Closed Won') {
                    //This is a custom error message, you may set it as per your need.
                    o.addError('Oh No! An Error Has Occured. Please try to save a record with Prospecting Stage');
                }
            }    
            else {
                if ( Trigger.isUpdate ) {
                    if(o.IsWon == true && Trigger.oldMap.get(o.id).IsWon == false) {
                        for (Opportunity oppty : [SELECT Account.Name, Owner.Name FROM Opportunity WHERE Id =:o.Id] ) {
                            OppAccName = oppty.Account.Name;
                            OppOwnerName = oppty.Owner.Name;
                        }    
                        status = OppOwnerName + ' just won ' + OppAccName + ' for ' + o.expectedrevenue + '!';
                            
                        post.ParentId = '0F9K000000005LR';
                        post.Title = o.Name;
                        post.Body = status;
                
                        insert post;
                    }
                    else {
                        return;
                    }
                }
            }
        }
    }    
}

 

This code has too many conditions, because I prefer to go for every scenario in singularly. And one more additional change, that you might require to do at your end is at code line 16.

 

I set a custom error message for user, so a user can understand error message and follow certain steps as described in that. You may change verbiage as per your policy.

 

Finally, this will work for you...

 

-Saw

 

 

CatieCatie

Hi Saw,

 

My apologies - I am very new at apex. I got to the point where I realized I need two sperate conditions - one for update and one for insert.Insert and update need two seperate conditions - but am not sure how to write them out. My apologies - I am in the very begining stages of lerning apex. 

 

  if(Trigger.isInsert || Trigger.isUpdate ) {
              //  if(o.IsWon == true && o.Amount >= 1000.00 && Trigger.oldMap.get(o.id).IsWon == false) {    ---will work for Trigger.isUpdate
                if(o.IsWon == true && o.Amount >= 1000.00) {     //will work for Trigger.isInsert

 

Could you help me arrange?

 

I think I almost figured it out- with some help but am stuck.

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

String status;
String OppAccName;
String OppOwnerName;
FeedItem post = new FeedItem();
    
    for(Opportunity o : Trigger.new) {
        if(o.OwnerId == '005d0000001TUC7') {
            return;
        }
        else {
            if(Trigger.isInsert || Trigger.isUpdate ) {
              //  if(o.IsWon == true && o.Amount >= 1000.00 && Trigger.oldMap.get(o.id).IsWon == false) {    ---will work for Trigger.isUpdate
                if(o.IsWon == true && o.Amount >= 1000.00) {     //will work for Trigger.isInsert
                    for (Opportunity oppty : [SELECT Account.Name, Owner.Name FROM Opportunity WHERE Id =:o.Id] ) {
                        OppAccName = oppty.Account.Name;
                        OppOwnerName = oppty.Owner.Name;
                    }

                    status = OppOwnerName + ' just won ' + OppAccName + ' for ' + o.expectedrevenue + '!';
                }
                else {
                    return;
                }
            }
            
            post.ParentId = '0F9K000000005LR';
            post.Title = o.Name;
            post.Body = status;
        }
    }    
    insert post;
}

          

spatelcespatelce

Hi Catie,

 

First...no need to apologize. None of us are born genius and we were also at begineer stage. Please feel free to post as many questions/doubts you get.

 

Good news...You can directly have opportunity with 'Closed Win'. I skipped single step and mention that it is not possible. Now, I have posted this code from collecting previous queries and my understanding. Just make sure before you use this code. Please uncheck box 'Is Active' for any other implemeted Trigger on Opportunity update for same purpose.

 

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

String status;
String OppAccName;
String OppOwnerName;
FeedItem post = new FeedItem();
    
    for(Opportunity o : Trigger.new) {
        if(o.OwnerId == '005d0000001TUC8') { //It will not post record for for this user to group.
            return;
        }
        else {
            if(Trigger.isInsert ) { 
                if( o.Amount >= 1000.00 && o.IsWon == true ) { //This will be executed on new record insertion with amount >= 1,000.00
                    for (Opportunity oppty : [SELECT Account.Name, Owner.Name FROM Opportunity WHERE Id =:o.Id] ) {
                        OppAccName = oppty.Account.Name;
                        OppOwnerName = oppty.Owner.Name;
                    }    
                    status = OppOwnerName + ' just won ' + OppAccName + ' for ' + o.expectedrevenue + '!';

                    post.ParentId = '0F9K000000005LR';
                    post.Title = o.Name;
                    post.Body = status;
                    
                    insert post;
                }
            }    
            else {
                if ( Trigger.isUpdate ) {
                    if( o.Amount >= 1000.00 && o.IsWon == true && Trigger.oldMap.get(o.id).IsWon == false) { //This will be executed on update to existing record with amount >= 1,000.00
                        for (Opportunity oppty : [SELECT Account.Name, Owner.Name FROM Opportunity WHERE Id =:o.Id] ) {
                            OppAccName = oppty.Account.Name;
                            OppOwnerName = oppty.Owner.Name;
                        }    
                        status = OppOwnerName + ' just won ' + OppAccName + ' for ' + o.expectedrevenue + '!';
                            
                        post.ParentId = '0F9K000000005LR';
                        post.Title = o.Name;
                        post.Body = status;
                        
                        insert post;      
                    }
                }
            }
        }
    }    
}

 

Please feel free to post as many questions/queries you get over this. Hope this will serve your purpose.

 

-Saw

This was selected as the best answer
CatieCatie
Thank you so so much! This was my first attempt and your help saved me. I really appreciate it!!!!!!!!! Thank you!
Lori L.Lori L.

Hi Saw,

I'm following this post and need a bit of help.  I hope you are still available.  

I don't use o.expected revenue - we use Amount but I'm getting a "Variable does not exist Amount" message on line19.

Also, what do I use for the Parent ID on line 21?

 

I appreciate any help as this too is my first Trigger!

 

Lori

spatelcespatelce

Hi Lori,

 

Congratulations for your first trigger.

 

Now, here is my best guess to use o.Amount should not give you error for line 19. (If you may share code then it will be easy to consider further steps in error)

 

and in line 21, where do you want to get a chatter post to be posted...i.e. Chatter Group, a person's chatter wall. So, you will require that 15-digit id.

 

Hope this may help you.

 

 

-Saw

Lori L.Lori L.

Cool! Thanks....I will add the Chatter Group ID.

Here's my code.  Pls. see if you can help me identify why it doesn't recognize amount.

 I got this message just trying to post this:

Your post has been changed because invalid HTML was found in the message body. The invalid HTML has been removed. Please review the message and submit the message when you are satisfied.

 

 

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

String status;
String OppAccName;
String OppOwnerName;

FeedItem post = new FeedItem();
    
    for(Opportunity o : Trigger.new) {
        if(o.OwnerId == '00580000004SwOg') { //It will not post record for for this user to group.
            return;
        }
        else {
            if(Trigger.isInsert ) { 
                if( o.Amount >= 1000.00 && o.IsWon == true ) { //This will be executed on new record insertion with amount >= 1,000.00
                    for (Opportunity oppty : [SELECT Account.Name, Owner.Name FROM Opportunity WHERE Id =:o.Id] ) {
                        OppAccName = oppty.Account.Name;
                        OppOwnerName = oppty.Owner.Name;
                     }    
                    status = OppOwnerName + ' just won ' + OppAccName + ' for ' + '!';

                    post.ParentId = '0F9K000000005LR';
                    post.Title = o.Name;
                    post.Body = status;
                    
                    insert post;
                }
            }    
            else {
                if ( Trigger.isUpdate ) {
                    if( o.Amount >= 1000.00 && o.IsWon == true && Trigger.oldMap.get(o.id).IsWon == false) { //This will be executed on update to existing record with amount >= 1,000.00
                        for (Opportunity oppty : [SELECT Account.Name, Owner.Name FROM Opportunity WHERE Id =:o.Id] ) {
                            OppAccName = oppty.Account.Name;
                            OppOwnerName = oppty.Owner.Name;
                        }    
                        status = OppOwnerName + ' just won ' + OppAccName + ' for ' + o.Amount + '!';
                            
                        post.ParentId = '0F9K000000005LR';
                        post.Title = o.Name;
                        post.Body = status;
                        
                        insert post;      
                    }
                }
            }
        }
    }    
}

spatelcespatelce

Lori,

 

Invalid HTML is a common message that we get while we paste a code in here. It is totally normal and harmless error.

 

Apparently it is working without any error and I couldn't see any error in your code. So, here is a suggestion that just delete current trigger and goto Opportunity object -> trigger -> new and simply paste this same code and quick save it.

 

It error still continues then logout from Salesforce instance and clear browser cache, history and for a safe side just restart your machine too. I know that it sounds totally stupid but we are working in browser and few times no error code shows error.

 

Now, this suggestion is extreme stupid but also check fields of Opportunity object to double-check standard field name Amount.

 

Trust me, I am neither making fun nor enjoying by making these comments. I also have faced nearly same error in past.

 

Hope this will make your code working because there is no error in code.

 

-Saw

Lori L.Lori L.
That is working now...went over to ie...also had to log in as someone else besides me! Duh! have a wonderful holiday and thanks again!
spatelcespatelce

Happy to hear that...Have fun in holiday..:)

Amanda ReamAmanda Ream
Hello Everyone, 
Another newbie here trying to create a trigger that posts when an Opp is won. I have no other requirements besides that the when an Opportunity is updated to the won stage, it posts a pedesigned message to a specific Chatter Group. I took the code from above and tried to modify it to do just that but I am running into a problem at the for line, with the error stating "Save error: Loop must iterate over collection type: Boolen". I tried to add an else statement to break and exit the loop but that gave me a bracket error. Am I totally off base here? Should I pulling a list first for the function to iterate over? Any help would be much appreciated!! 

trigger ChatterPostWonOpp on Opportunity (after update) {

String status;
String OppAccName;
String OppOwnerName;

FeedItem post = new FeedItem();
   
    for(Opportunity o : Trigger.isupdate) {
                if(o.IsWon == true ) //This will be executed on new record insertion when Opp is won
                    for (Opportunity oppty : [SELECT Account.Name, Owner.Name FROM Opportunity])
                     {
                        OppAccName = oppty.Account.Name;
                        OppOwnerName = oppty.Owner.Name;
                      }   
                 
                    status = OppOwnerName + ' just won ' + OppAccName + ' for ' + o.Amount + '!';

                    post.ParentId = '0F9g00000008b7c';
                    post.Title = o.Name;
                    post.Body = status;
                   
                    insert post;
          }
    }