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
Victor PVictor P 

0% code coverage for trigger and List index out of bounds: 0 error

Hi all,

I ran the test class and I get 0% code coverage and an error only when running the test. The trigger works perfectly fine in the sandbox.
Can someone point me in the right direction? 

The triggers loops through all Products in an Opportunity and concatenates all Products in one custom field created in the Opportunity Object.
 
trigger OpportunityConcat on Opportunity (before insert, before update) {

list<opportunity> oppt = trigger.new; 

public static String isEmptyString(String inputString) 
{ 
String str = String.isBlank(inputString)?'': inputString;
return str;
}

if ( trigger.isInsert || trigger.isUpdate){
list<opportunityLineItem> soli = new list<opportunityLineItem>([select id ,opportunityId, opportunity.name, product2.name,Purchase_Order__c, (select id from opportunitylineitemschedules ORDER BY ScheduleDate ASC), Description__c from opportunitylineitem where opportunityId =: trigger.new[0].id]); 
string allProducts='';
if (soli != null && soli.size()>0){
for(opportunityLineItem opp : soli){
 allProducts += opp.id +','+ opp.opportunityid +','+ isEmptyString(opp.opportunity.name) +','+ isEmptyString(opp.product2.name) +','+ isEmptyString(opp.Purchase_Order__c) +','+ isEmptyString(opp.Description__c ) +','+ opp.opportunitylineitemschedules[0].id +'\n';
}

for(Opportunity opp : trigger.new){
 opp.ConcatProd__c = allProducts;
}
}
else
{
for(Opportunity opp : trigger.new){
opp.ConcatProd__c = '';
}
}
}
}



 
Victor PVictor P
Error is:
System.DmlException: Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, OpportunityConcat: execution of BeforeUpdate

caused by: System.ListException: List index out of bounds: 0

Trigger.OpportunityConcat: line 16, column 1: []

 
Andrew GAndrew G
HI
The error indicates that a list reference contains no values . In this case on line 16 the only list reference I see is :  opp.opportunitylineitemschedules[0].id

Drop that from the code temporarily to confirm that it is the cause of your issues.
If it is, then modify your code to create a Map a Opportunity Line items (key the Map by the OpportunityLineItem, and then either the first schedule for each, or create a list of schedules for each)
Then when you do the concatenate, use the .get method on the Map.  Example mapOLISs.get(opportunityLineItem.Id)

Regards
​​​​​​​Andrew
Andrew GAndrew G
And re-reading the question, does your test class insert the OpptyLineItemSchedule?

Can you post your test class for review please?

Regards
Andrew
 
Victor PVictor P

Hi @Andrew G, How do I key the map with the first schedule for each?

This is what I' tried:
 
Map<OpportunityLineItemSchedule, Integer> mapa = new Map<OpportunityLineItemSchedule, Integer>();

for (OpportunityLineItemSchedule a : [select id, ScheduleDate from opportunitylineitemschedule where OpportunityLineItemId =: soli[0].id ORDER BY ScheduleDate ASC LIMIT 1]) {
    mapa.put(a, 1);   
}
 
//then concatenate with:
mapa.get(opp.opportunitylineitemschedules[0].id)

Alternatively, wouldn't be easier to check if the second list is empty? (opportunitylineitemschedule)
 
Andrew GAndrew G
Ok,
The underlying issue is the order of creation of the records e.g. Opportunity, then Opportunity Product, then Schedule.  When querying the product, the schedule is not always present.

So a slight code rewrite.
Yes, just testing the list would be easier, so I have written it that way.
I have also adjusted the naming convention - your re-use of the 'opp' variable leads to some confusion. 
 
Trigger OpportunityConcat on Opportunity (before insert, before update) {
	list<opportunity> oppt = trigger.new; 

	public static String isEmptyString(String inputString) { 
		String str = String.isBlank(inputString)?'': inputString;
		return str;
	}

	if ( trigger.isInsert || trigger.isUpdate){
		list<OpportunityLineItem> oliList = new list<OpportunityLineItem>([SELECT id ,opportunityId, opportunity.name, product2.name,Purchase_Order__c, (select id from opportunitylineitemschedules ORDER BY ScheduleDate ASC), Description__c 
			FROM opportunitylineitem WHERE opportunityId =: trigger.new[0].id]); 
		String allProducts='';
		if (oliList != null && oliList.size()>0){
			for(OpportunityLineItem oli : oliList){
				if (oli.opportunitylineitemschedules.size() > 0 ) {
					allProducts += oli.id +','+ oli.opportunityid +','+ isEmptyString(oli.opportunity.name) +','+ isEmptyString(oli.product2.name) +','+ isEmptyString(oli.Purchase_Order__c) +','+ isEmptyString(oli.Description__c ) +','+ oli.opportunitylineitemschedules[0].id +'\n';  //
				} else {
					allProducts += oli.id +','+ oli.opportunityid +','+ isEmptyString(oli.opportunity.name) +','+ isEmptyString(oli.product2.name) +','+ isEmptyString(oli.Purchase_Order__c) +','+ isEmptyString(oli.Description__c ) +'\n';  //+','+ oli.opportunitylineitemschedules[0].id 
				}
			}
			for(Opportunity opp : trigger.new){
				opp.ConcatProd__c = allProducts;
			}
		} else {
			for(Opportunity opp : trigger.new){
				opp.ConcatProd__c = '';
			}
		}	
	}

}
I also note that your code is not bulkified.  I have not bulkfied, but consider what would happen if you (or someone) did a dataload on Opportunities. Basically, all the Opportunities would have the information for the first Oppty in the load list.

And finally, is there a reason why you are pulling that data from the children into the parent?  As concatenated data, it would become harder to query for other processes.

Regards
Andrew​​​​​​​