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
BillPowell__cBillPowell__c 

Assign index # to quote line items help??

I'd like to find a way to dynamically assign a "Line Number" to a quote line based on certain criteria. 

For instance, If I have 100 lines on my quote, and only want to show 20 of them and assign a line # 1-20 to those 20 items, how can I accomplish this? The "hidden" quote line items will not show up on the quote template since they are for internal configuration use only, but are throwing off the line #'s since they are hidden, its causing the line #'s for shown items to jump around. 

I'm assuming this may be an "After" trigger on the quote object? I don't know code well but I should be able how to write the trigger if need be with some guidance. 

Am I on the right path of having an "After" trigger and having it cycle through "quote lines" and if "Core Product" is checked and "Line Number" is blank, assign it 1, then go through the loop again and have X++ so the next quote line if it meets criteria, is 2, and so on and so on? 

Thoughts? Thanks for your help 

 
Mike.KatulkaMike.Katulka
Hey Bill! 

You are spot on, after trigger all the way.  There is a trick though.  Salesforce tells you that you can't update the records in the trigger in an after trigger so see the code sample.  I only spent 25 minutes writing this so it's very rough but for sure will get you going.  Sorry for any error-goodies I may have left in there.  Let me know how it goes.

If I mis-understood this at all let me know.  This will increment Quote Line Items starting from 1 within each Quote... and if there were existing line items with numbers already (gaps and all) it will still add 1 to the highest record.

Your unit test should simply create a Quote, insert various Quote Line Items and perform different things like deletes, adds etc.  Then Assert that the correct outcome is present.

Also, this isn't a proper "trigger pattern" which ultimately you should be doing for best practice.  Typical pattern puts all the business/code logic into another class and the trigger is just a shell to pass trigger.new and trigger.newMap to the class method.  You'd also want to add custom settings so that you can disable the trigger with a click as needed.
 
trigger QuoteLineItemTrigger on QuoteLineItem (after insert, after update){
	
	// prevent recursion
	if (!QuoteLineItemUtility.TriggerIsRunning){
		QuoteLineItemUtility.TriggerIsRunning = true;

		Set<Id> QuoteIds = new Set<Id>();

		List<QuoteLineItem> lineItemsForUpdate = new List<QuoteLineItem>();

		// gather IDs first of quotes
		for (QuoteLineItem ql : trigger.new) {
			
			// if this is a core product and these are either NEW line items or old ones that have changed
			if (ql.CoreProduct__c == true && 
					(trigger.isInsert || trigger.oldMap.get(ql.id).CoreProduct__c != true) {
				// gather IDs so that we can query for all quote lines later
				QuoteIds.add(ql.QuoteId);
			}
		}

		if (!QuoteIds.isEmpty()) {
			// get all line items for all of the above quotes in bulk so that we don't hit governor limits
			Id prevQuoteId;
			Integer lastLineNumber;
			// we can query all and these triggered records in an after trigger, nothing missing
			for (QuoteLineItem ql : [Select Id, LineNumber__c
									   From QuoteLineItem 
									  Where QuoteId in QuoteIds
									    AND CoreProduct__c = true
								   Order By QuoteId, LineNumber__c NULLS LAST]) {

				// These are coming in already grouped by the same quote
				// and are sorted by the line number within that group
				
				if (prevQuoteId == null || prevQuoteId != ql.QuoteId) {

					// start fresh because this is the first iteration OR we made it to 
					// another group of line items
					prevQuoteId = ql.QuoteId;
					lastLineNumber = 0; //back to default					
				}

				// Set the new line numbers as needed
				if(ql.LineNumber__c == null || ql.LineNumber__c == 0) {

					ql.LineNumber__c = lastLineNumber + 1;
					
					// Need to create a "new" in memory line item to break reference to the 
					// trigger reference, otherwise you will get an error like "cannot update in an After trigger"
					lineItemsForUpdate.add(new QuoteLineItem(id = ql.id, LineNumber__c = ql.LineNumber__c));
				}
				
				lastLineNumber = ql.LineNumber__c;

			} // end for

		}

		if (!lineItemsForUpdate.isEmpty())
			update lineItemsForUpdate; // DML only the changed line items

		QuoteLineItemUtility.TriggerIsRunning = false;
	}
}



public class QuoteLineItemUtility(){
	public static TriggerIsRunning = false;
}

 
BillPowell__cBillPowell__c
Thanks Mike, we decided to reconfigure how we were going to display these line items, and wound up not needing a trigger. But I saved this for the future :-)