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
Miller 95Miller 95 

Apex Trigger and Testing Question

Hi,

I am new to trigger creation and have mostly built what I wanted which is to have my trigger create a quote automatically once the sales Rep creates the opportunity. The only thing that is missing si to have the trigger also open up that quote that was just created, so the structure would be: Open new Opportunity -> input data -> Click save -> Trigger executes and creates a new quote from that opportunity -> the sales rep lands within the quote ready to select the products (this part is missing.

Not sure if this is possible for a triggers. Here is what I have:

trigger QuoteCreator on Opportunity (after insert, after update) {
Map<Quote, Id> quoteMap = new Map<Quote, Id>();
for (Opportunity o : Trigger.new) {
Quote q = new Quote();
q.name = 'Quote-' + o.name;
q.opportunityId = o.id;
insert q;
quoteMap.put(q, o.accountId);
}
Account[] accList = [SELECT
billingStreet,
billingCity,
billingState,
billingCountry
FROM
Account
WHERE
id IN :quoteMap.values()];
for (Quote quote : quoteMap.KeySet()) {
for (Account a : accList) {
if (quoteMap.get(quote) == a.Id) {
quote.billingStreet = a.billingStreet;
quote.billingCity = a.billingCity;
quote.billingState = a.billingState;
quote.billingCountry = a.billingCountry;
}
update quote;
}
}
}


The second part is creating a test class for it so I can get it moved into production. The Trigger works great within the sandbox, but the testing is needed from what I know about trigger dev into production. Please let me know if I am even doing it right. Here is what I have:

@isTest
private class QuoteCreator {

static testMethod void QuoteCreator() {

    Map<Quote, Id> quoteMap = new Map<Quote, Id>();
       
for (Opportunity o : Trigger.new) {
Quote q = new Quote();
q.name = 'Quote-' + o.name;
q.opportunityId = o.id;
    insert q;
quoteMap.put(q, o.accountId);
    }
   
    test.startTest();

    update quoteMap;

    test.stopTest();
}
}
Bradley DelauneBradley Delaune
Hi Miller 95,
This is not possible with a trigger because triggers are not directly linked to the Salesforce UI.  Creating an opportunity can happen from anywhere (UI or any of the various APIs available).  The only way to do this is to override the default Opportunity "New" page with a custom visualforce page and controller.  You would have to override the standard "save" method in the controller and manually insert the quote in that method in order to get the ID of the of quote so you could send them to the desired page.  

That may be a bunch of jibber-jabber to you, but overall, I would say avoid it.  It isn't a pretty solution and it would be very difficult to maintain.  Urge your Sales team to simply click on the newly created quote after inserting the opporunity.

I hope that helps!
Bradley DelauneBradley Delaune
On top of that, your trigger could use some work.

See this code:
trigger QuoteCreator on Opportunity (after insert, after update) {
    set<Id> accIdSet = new set<Id>();
    for (Opportunity o : Trigger.new)
    {
        accIdSet.add(o.AccountId);
    }
    
    map<Id,Account> accountMap = new map<Id,Account>([select Id,
                                                    billingStreet,
                                                    billingCity,
                                                    billingState,
                                                    billingCountry
                                                From Account
                                                Where Id in :accIdSet]);
    
    List<Quote> quoteList = new list<Quote>();
    for (Opportunity o : Trigger.new)
    {
        Quote q = new Quote();
        q.name = 'Quote-' + o.name;
        q.opportunityId = o.id;
        if(accountMap.containskey(o.AccountId))
        {
            q.billingStreet = accountMap.get(o.AccountId).billingStreet;
            q.billingCity = accountMap.get(o.AccountId).billingCity;
            q.billingState = accountMap.get(o.AccountId).billingState;
            q.billingCountry = accountMap.get(o.AccountId).billingCountry;
        }
        quoteList.add(q);
    }
    
    insert quoteList;
}

The idea here is that you don't want to insert quotes and then update them.  Ideally you insert all of them at once with all the information they need from the beginning.  Here I first gather all of the Account IDs, then gather the billing information from those records, then go through the opporunities and create quotes for all of them.  Finally, I insert all of the quotes at once at the end.

You test class should look a little different as well:
@isTest
private class QuoteCreator {

	static testMethod void QuoteCreator() {
		Account a = new Account();
		a.Name = 'test account';
		//fill in billing address info
		insert a;
		
		
		list<Opportunity> oppList = new list<Opportunity>();
		for(Integer i = 0; i < 200 ; i++)
		{
			Opportunity o = new Opportunity();
			o.AccountId = a.Id;
			o.Stage = 'Prospecting';
			//fill in other required fields here
			oppList.add(o);
		}
		Test.startTest();
		insert oppList;
		Test.stopTest();
		
		list<Quote> quoteList = [select Id, Name, Billing info... from Quote];
		
		for(Quote q : quoteList)
		{
			System.assertEquals(a.billingcity,q.billingcity);
			System.assertEquals(a.billingInfo,q.billingInfo);
			...
		}
	}
}

The idea here is that you want to actually test the functionality of the Opportunity trigger.  You want to insert opportunities and check that quotes are inserted.  I first set up the account, then 200 opportunities.  After inserting the opportunities (notice the startTest/stopTest is around the functionality we are testing), I query for quotes and verify they have the right info.

Test classes do not share data with your org (and data is rolled back when the test is finished executing), so querying for all quotes will only give you quotes inserted in your test class.

Let me know if this helps!
Miller 95Miller 95
Thanks Bradley, that was clear. I thought it was not possible without going in a changing the functionality of the solution. I just wanted to make sure I didn't over look anything.

Now on the testing piece; am I going about it the right way or am I way off? When trying to get it moved into production I get the typical erros: 0% tested and need 1%, also overall of 73% class tested and you need 75%.
Bradley DelauneBradley Delaune
Hi Miller, Looks like I was answering your question while you were asking it :-)   Let me know if you need more clarification.

One final comment, you should probably remove your "after update" from the trigger.  I would hate to see a new quote created EVERY time an opportunity was updated.