+ Start a Discussion
Shannon Andreas 1Shannon Andreas 1 

Trigger not working. Test Class not working. Please help

I have a trigger and am trying to write a test class (see below). The trigger is not working...I have asked for help with no resolution. I am asking again for help with the trigger as well as test class (which is not working either). I think my issue is with the Account ID and Opportunity ID part. 

All I am trying to do is have a contract created when a checkbox on the oppty is checked. 

Please see previous post: https://developer.salesforce.com/forums/ForumsMain?id=906F0000000BTEhIAO

TRIGGER:

trigger CreateContract on Opportunity (after insert) {
 private List<Contract> ctr = new List<Contract>();
 
  for(Opportunity o : Trigger.new) {
    if(o.Ready_for_Contract__c == true) {
      Contract c = new Contract(Name = o.Name,
                                Status = 'Draft',
                                Total_Contract_Value__c = o.Total_Sales_Price_of_Products__c,
                                StartDate = o.Contract_Start_Date__c,
                                Payment_Status__c = 'Ready to Be Invoiced',
                                AccountId = o.AccountId,
                                Opportunity_Name__c = o.Id);
        ctr.add(c); 
      }
      insert ctr;
     }    
}

TEST Class:

@isTest
private class TestClassCreateContractTrigger 
{
    static testMethod void validateCreateContract() 
    {       
       Contract c = new Contract(
            Name='Test Account',
            Status = 'Activated',
            Total_Contract_Value__c = decimal.valueof('6995'),
            StartDate = System.Today(),
            Payment_Status__c = 'Ready to Be Invoiced',
            AccountId = Opportunity.AccountId,
            Opportunity_Name__c = Opportunity.Id);
       insert c;
       
      }
}

Thanks in advance for your help!!

Shannon
Best Answer chosen by Shannon Andreas 1
Morgan MarcheseMorgan Marchese
Code Coverage within the Salesforce UI is a little wonky, I don't always trust it. Also any time you edit your code even if you just edit/save, it will reset your code coverage to 0% until you run your test class again.

Visibility of the field shouldn't matter because regardless of if you can see it or not, the data still gets populated in the SFDC database. 

Your trigger is currently set as "after insert". Are you checking the box during opp creation, or are you checking the box on a pre-existing opp? If you are UPDATING an existing opp, then after insert is not going to cut it. Maybe change it to:
trigger CreateContract on Opportunity (after insert, after update) {

All Answers

Shannon Andreas 1Shannon Andreas 1
Also...

There is no "error" on the trigger, just does not create the contract.

I am receiving compile errors on the test class:

[Error] Error: Compile Error: Invalid initial expression type for field Contract.AccountId, expecting: Id at line 12 column 25

When I change anything on line 12 or 13, I get errors. So if I "fix" 1 line, the other errors.
 
Morgan MarcheseMorgan Marchese
This may not be your only problem, but your test class probably isn't working because you're trying to set fields using a reference to an Opportunity, but you haven't created an opportunity in your test class. If you don't create and insert the opportunity first, then your contract has no opportunity to reference when setting AccountId and Opportunity_Name__c.

I just wrote this up really quick so it may not be 100% accurate, but you should try something like this... we first create and insert a test account, then we create and insert a test opp linked to that account, and then finally we create and insert your contract:
 
@isTest
private class TestClassCreateContractTrigger 
{
    static testMethod void validateCreateContract() 
    {       

      Account a = new Account(
      Name='Test Account');

      insert a;

      Opportunity o = new Opportunity(
            Name='Test Opp',
            CloseDate = Today(),
            Account = a.Id,
            Stage = 'New',
            Amount = decimal.valueof('6995'));

      insert o;

       Contract c = new Contract(
            Name='Test Contract',
            Status = 'Activated',
            Total_Contract_Value__c = o.Amount,
            StartDate = System.Today(),
            Payment_Status__c = 'Ready to Be Invoiced',
            AccountId = o.AccountId,
            Opportunity_Name__c = o.Id);

       insert c;
       
      }
}

 
Morgan MarcheseMorgan Marchese
You can also remove the word "private" in front of List<Contract> in your trigger
 
private List<Contract> ctr = new List<Contract>();

Would become...
List<Contract> ctr = new List<Contract>();

Apex is also run with the permission level of whomever triggered it, so make sure that the user executing this trigger has access to create new contracts. If you are not getting errors when saving the trigger then that means the context of your trigger appears valid according to SFDC. I'm not an expert yet but I don't see anything wrong with your trigger other than the unnecessary 'private' in front of the list.

Also, as stupid as it sounds - make sure your trigger is marked as active ;)

If all else fails, go to Setup > Logs > Debug Logs and add your user as a Monitored User, and then go create an opportunity that meets your IF criteria. If a contract is not created, check the debug logs for your user - one of them will reveal some sort of error or reason why it wasn't executed. 
 
Shannon Andreas 1Shannon Andreas 1
Thank you Morgan! I will give it a try. Makes sense and had a feeling that I was missing something on the test class. I was asking myself how the system would know it was coming from an oppty!

Also to your second and third points...I did!! After racking my brain for a while, I figured I would try the simplest of causes (Ocham's razor). Unfortunately, the simple answer was not right! I already set myself up in the logs as well. Great point on user accessibility as well! Never thought about it.

Will let you know!!

Shannon
Shannon Andreas 1Shannon Andreas 1
Good news is my test class works great :)

Bad News is my trigger is failing. I am pretty sure it is because of how I have the account and opportunity name set up:

System.DmlException: Insert failed. First exception on row 0; first error: REQUIRED_FIELD_MISSING, Required fields are missing: [AccountId, Opportunity_Name__c]: [AccountId, Opportunity_Name__c]

Any help is appreciated.

Shannon
Morgan MarcheseMorgan Marchese
Hey Shannon,

I don't use contracts so I wasn't familiar with them, but I took a look in our sandbox and I see that the "Account Name" fields API name is just "Account", rather than "AccountId". As far as Opportunity_Name__c, that's a custom field that your org created on your contracts, so double check the custom fields API name to make sure it is the exact spelling seen here. If it is a lookup field to opportunity, then o.Id should work.

Try this:
 
trigger CreateContract on Opportunity (after insert) {
 private List<Contract> ctr = new List<Contract>();
 
  for(Opportunity o : Trigger.new) {
    if(o.Ready_for_Contract__c == true) {
      Contract c = new Contract(Name = o.Name,
                                Status = 'Draft',
                                Total_Contract_Value__c = o.Total_Sales_Price_of_Products__c,
                                StartDate = o.Contract_Start_Date__c,
                                Payment_Status__c = 'Ready to Be Invoiced',
                                Account = o.Account,
                                Opportunity_Name__c = o.Id);
        ctr.add(c); 
      }
      insert ctr;
     }    
}

 
Shannon Andreas 1Shannon Andreas 1
I thought I had it working! Still not creating the contract. Wondering if there is a setting in my system that is not allowing the contract to be created without something else?
Morgan MarcheseMorgan Marchese
Not sure unfortunately - as I mentioned earlier, my org doesn't use contracts so I'm working 'off-the-cuff' on this. Have you tried manually creating a contract to see if there are additional required fields or maybe validation rules that aren't being satisfied when the apex runs?
Shannon Andreas 1Shannon Andreas 1
I just did that and everything is accounted for. There is a validation rule against the contract record, but I turned it off when I started this...so bummed! I don't understand why it is not working. The test class passed...I don't know what could be blocking it. What is also weird is that the debugger is stating that my actions are successful!

The checkbox is hidden from users because it is used as a behind the scenes trigger to start the CreateContract trigger. I can see it...could this be a problem? It is not a read only, but can only be seen by me.
Shannon Andreas 1Shannon Andreas 1
Weird too is that the code coverage keeps changing?
Morgan MarcheseMorgan Marchese
Code Coverage within the Salesforce UI is a little wonky, I don't always trust it. Also any time you edit your code even if you just edit/save, it will reset your code coverage to 0% until you run your test class again.

Visibility of the field shouldn't matter because regardless of if you can see it or not, the data still gets populated in the SFDC database. 

Your trigger is currently set as "after insert". Are you checking the box during opp creation, or are you checking the box on a pre-existing opp? If you are UPDATING an existing opp, then after insert is not going to cut it. Maybe change it to:
trigger CreateContract on Opportunity (after insert, after update) {
This was selected as the best answer
Shannon Andreas 1Shannon Andreas 1
OMG... MUUUUAAH !

After Insert worked. After update gave me an error when I tried to save the opportunity.The only thing I need to figure out now is why it is not attaching to the Account record. They should all be tied together...or so I thought. There could be a delay. Also need to add billing info since there is a validation that requires entry of this information on the Contract. It's also only getting 33% coverage. I heard the same thing from other people about not "trusting" the system...

Here is the correct code:

Trigger:

trigger CreateContract on Opportunity (after insert) {
    List<Contract> ctr = new List<Contract>();
    
      for(Opportunity o : Trigger.new) {
        if(o.Ready_for_Contract__c == true) {
          Contract c = new Contract(Name = o.Name,
             Status = 'Draft',
             Total_Contract_Value__c = o.Amount,
             StartDate = o.Contract_Start_Date__c,
             Payment_Status__c = 'Ready to be Invoiced',
             Account = o.Account,
             Opportunity_Name__c = o.Id);
         ctr.add(c);
         }
      insert ctr;
      }
}


Test class:

@isTest
private class TestClassCreateContractTrigger 
{
    static testMethod void validateCreateContract() 
    {   
       Account a = new Account(
       Name = 'Test Account');
    insert a;

       Opportunity o = new Opportunity(
       Name = 'Test Opp',
       CloseDate = System.Today(),
       AccountId = a.Id,
       StageName = 'Signed / Closed Sale',
       Amount = decimal.valueof('6995'));
    insert o;
    
       Contract c = new Contract(
            Name='Test Contract',
            Status = 'Draft',
            Total_Contract_Value__c = o.Amount,
            StartDate = System.Today(),
            Payment_Status__c = 'Ready to be Invoiced',
            AccountId = o.AccountId,
            Opportunity_Name__c = o.Id);
       insert c;
       
      }
}

Thank you so very much Morgan! Hopefully I will not have any more issues with adding the other fields.

Shannon
Shannon Andreas 1Shannon Andreas 1
Just another question:

The billing information is coming from the Account object. It is not stored in the Opportunity. Is there anything special that I need to do to add that object to the trigger? Other than writing it as a.BillingCity, a.BillingState, etc? 

I have a feeling I do?
Morgan MarcheseMorgan Marchese
Hey Shannon,

Test class code coverage is determined by how much of your real code is invoked by your test class. 33% is obviously much too low, but your code isn't very complex so getting it above the required 75% should be easy.

As I mentioned originally, my test class was a loose example, so it wasn't 100% accurate to your use case. In the case of your trigger, your entire execution is based off of o.Ready_For_Contract__c being set to True, but we aren't setting that field to true on your Opp in your test class, so it's likely not executing a big chunk of it - thus, 33%.

Try just adding Ready_For_Contract__c = true (API name is case sensative, so make sure its all cased correctly), to the opportunity creation portion of your test class and let's see where that gets you with code coverage:
 
Opportunity o = new Opportunity(
       Name = 'Test Opp',
       Ready_For_Contract__c = true,
       CloseDate = System.Today(),
       AccountId = a.Id,
       StageName = 'Signed / Closed Sale',
       Amount = decimal.valueof('6995'));
    insert o;

As for why it's not linking to the account, like I said, I'm not an expert yet - still learning myself (I just like to help when I can as I feel that it improves my knowledge while assisting others). After reviewing some code in my own org I believe I was originally incorrect in telling you to use Account instead of AccountId. Try changing Account = o.Account in your trigger back to AccountId = o.AccountId again and see if that works - there shouldn't be any delay with linking during apex triggering. it's all or nothing.

Regarding bringing in extra fields from account (Billing info), we can definitely do this, but it will add more complexity to your trigger, and also might not be an ideal scenario if you think about it long term. Let's say a customer has 3 opportunities created in their account lifecycle - but their billing address changed twice. Do you want all 3 contracts to have different billing addresses on them? OR do you want the billing address to update dynamically based on the billing address that currently exists on the account? If you want the address to be different on each, then we'd want to use a trigger to fill in the data - but if you want it to be their most recent billing info then you can remove the validation rule from contract and put it on account instead and then just use formula fields to bring in the data from account to contract once you get the lookup relationship fixed. Make sense? Let me know what you're thinking.