• Kyle Golik
  • NEWBIE
  • 10 Points
  • Member since 2016

  • Chatter
    Feed
  • 0
    Best Answers
  • 0
    Likes Received
  • 0
    Likes Given
  • 4
    Questions
  • 2
    Replies
IF ( [Quote].TotalPrice <= 19999.99([Quote].TotalPrice=[Quote].TotalPrice * 1 ) 
IF ( [Quote].TotalPrice >= 20000.00 && [Quote].TotalPrice <= 39999.99([Quote].TotalPrice=[Quote].TotalPrice * 0.98) 
IF ( [Quote].TotalPrice >= 40000.00 && [Quote].TotalPrice <= 59999.99([Quote].TotalPrice=[Quote].TotalPrice * 0.96)  
IF ( [Quote].TotalPrice >= 60000.00 && [Quote].TotalPrice <= 79999.99([Quote].TotalPrice=[Quote].TotalPrice * 0.94) 
IF ( [Quote].TotalPrice >= 80000.00 && [Quote].TotalPrice <= 99999.99([Quote].TotalPrice=[Quote].TotalPrice * 0.92) 
IF ( [Quote].TotalPrice >= 100000.00 && [Quote].TotalPrice <= 119999.99([Quote].TotalPrice=[Quote].TotalPrice * 0.90) 
IF ( [Quote].TotalPrice >= 120000.00 && [Quote].TotalPrice <= 139999.99([Quote].TotalPrice=[Quote].TotalPrice * 0.88) 
IF ( [Quote].TotalPrice >= 140000.00 && [Quote].TotalPrice <= 159999.99([Quote].TotalPrice=[Quote].TotalPrice * 0.86) 
IF ( [Quote].TotalPrice >= 160000.00 && [Quote].TotalPrice <= 179999.99([Quote].TotalPrice=[Quote].TotalPrice * 0.84) 
IF ( [Quote].TotalPrice >= 180000.00 && [Quote].TotalPrice <= 199999.99([Quote].TotalPrice=[Quote].TotalPrice * 0.82) 
IF ( [Quote].TotalPrice >= 200000.00 ([Quote].TotalPrice=[Quote].TotalPrice * 0.80)  
ELSE NULL ) ) ) ) ) ) ) ) ) ) )

Our company is trying to unveil new discount opportunities to our clients and I have implemented frequency/volume pricing on products, loyalty pricing. One last discount they are looking for is the ability to discount the entire quote/opportunity.

Through research I learned without CPQ the best way is to use a workflow in process builder.

Above is the formula that I am getting the error "The formula expression is invalid: Syntax error. Missing ')'" I have reviewed several times and believe each parenthesis is accounted for. Only other thing I can think of is the way I am trying to use the ELSE clause is confusing SF.

So my questions are:

#1 Is my thought process through Process Builder sound or is there a better way?

#2 More applicable to this discussion section - what am I erroring out on here in the sample code?

Thank you in advance

Goal: To create a component with VisualForce that will display three columns and function. 

Need: A component built with VisualForce to display three columns for the Sales Manager to split commission on an Opportunity. Ideally will be 3 by 3. The first column will be the Sales Rep column where the Sales Manager will look up the Sales Rep - can always change it to a Text field but ideally lookup. Second column will be the percentage of commission. Third column would be a formula field that displays the total commission for the rep. 

Usually in most use cases there are only two sales reps, why we need three is now with our organization's digital campaigns, a digital specialist is spending time on the sale and it has been agreed that digital specialist get a commission off the sale. The rows (just to specify) are for each rep. 

The Error: 

Error: Attribute value in <apex:inputField> must contain only a formula expression that resolves to a single controller variable or method in Commission_Split at line 14 column 80

The Code: 

<apex:component>
 <apex:pageBlockSection title="Commission Splits" columns="3">

              
<apex:pageBlockSectionItem dataStyle="width:10%" labelStyle="width:23%" >
<apex:outputLabel >Sales Rep</apex:outputLabel>
<apex:inputField value="{! Opportunity.Commission_1__c}" style="width:80px"/>
<apex:inputField value="{! Opportunity.Commission_2__c}" style="width:80px"/>
<apex:inputField value="{! Opportunity.Commission_3__c}" style="width:80px"/>
</apex:pageBlockSectionItem>

<apex:pageBlockSectionItem dataStyle="width:10%" labelStyle="width:23%" >
<apex:outputLabel >Precent of Commission/apex:outputLabel>
<apex:inputField value="{! Opportunity.Percent_Rep_1__c}%" style="width:80px"/>
<apex:inputField value="{! Opportunity.Percent_Rep_2__c}%" style="width:80px"/>
<apex:inputField value="{! Opportunity.Percent_Rep_3__c}%" style="width:80px"/>
</apex:pageBlockSectionItem>

<apex:pageBlockSectionItem dataStyle="width:10%" labelStyle="width:23%" >
<apex:outputLabel >Total Commission</apex:outputLabel>
<apex:outputField value="{Opportunity.Total_Rep_1__c}" style="width:80px"/>
<apex:outputField value="{Opportunity.Total_Rep_2__c}" style="width:80px"/>
<apex:outputField value="{Opportunity.Total_Rep_3__c}" style="width:80px"/>
</apex:pageBlockSectionItem>

</apex:pageBlockSection>          
</apex:component>

Trying to deploy this trigger and as we all know there is a prerequisite for Salesforce for Code Coverage on your Trigger. I have created a Test Class for this Trigger and the problem I have is there are no Test Methods, I thought I established a method in the Test Class. 

Apex Test Class
 

@isTest
private class UpdateOpportunityProductMonthsToFulfill{

    @testSetup 
    static void testOLIUpdate()
    {
        // Obtain Apex governor limits and resources for this test
        Test.startTest();

        OpportunityLineItem oli = new OpportunityLineItem(Months_to_Fulfill__c='');
        insert oli;

        QuoteLineItem qli = new QuoteLineItem(OpportunityLineItemId=oli.Id);
        insert qli;

        qli.Year_Served_First__c='2018';
        qli.Months_Served_Y1__c='May;June';
        update qli;
        
       system.assertEquals(oli.Months_to_Fulfill__c, 'May 2018; June 2018');

        // Release governor limits and resources
        Test.stopTest();
    }
}


Apex Trigger​

 

trigger UpdateOpportunityProductMonthsToFulfill on QuoteLineItem (after insert, after update) {
    String monthsToFulfill = '';
    map<String, String> updateMap = new map<String, String>();
    
    // This is where the text for the Months_To_Fulfill__c field is built
    for(QuoteLineItem qli : Trigger.new){
        // To make a spelled-out list of months & years instead of the older "start through end"
        
        List<String> yearOneMonths = qli.Months_Served_Y1__c.replaceAll('None(;)?', '').split(';');
        for(String month : yearOneMonths){
            monthsToFulfill += ( monthsToFulfill.length()==0 ? '' : '; ' ) + month + ' ' + qli.Year_Served_First__c;
        }
        if(!(qli.Year_Served_Second__c == null || qli.Year_Served_Second__c.equals('None'))){
            List<String> yearTwoMonths = qli.Months_Served_Y2__c.replaceAll('None(;)?', '').split(';');
            for(String month : yearTwoMonths){
                monthsToFulfill += '; ' + month + ' ' + qli.Year_Served_Second__c;
            }
        }
        
        updateMap.put(qli.OpportunityLineItemId, monthsToFulfill);
    }
  
    for(String oliID : updateMap.keySet()){
        // This is the calculated value we saved from looping through the Trigger.new Collection
        monthsToFulfill = updateMap.get(oliID);
        
        // Get the OpportunityLineItems associated with this QuoteId
        List<OpportunityLineItem> oliList = [Select Id, OpportunityId, Months_To_Fulfill__c 
                                             From OpportunityLineItem Where Id  = :oliID ];
        
        List<OpportunityLineItem> olisToUpdate = new List<OpportunityLineItem>();
        
        if(oliList.size() > 0){
            for (OpportunityLineItem oli : oliList){
                // Populate this olisToUpdate list because we may 
                // want to get smarter about what's actually updated 
                // in the future.  For now, everything is updated.
                oli.Months_To_Fulfill__c = monthsToFulfill;
                olisToUpdate.add(oli);
            }
            
            // Batch update the OpportunityLineItems we identified earlier
            if(olisToUpdate.size() > 0){
                update olisToUpdate;
            }
        }
    }     
}

Background:
Our company works in print media and when we sell ads for our magazine, our reps fill the fulfillment information out at the Quote Line Item object.

Our accounting software can only read from the Opportunity Product level.

I have created a trigger that achieves that with one catch, it doesn't recognize when a rep needs to sync the primary quote with the Opportunity.

The button is a standard Salesforce button, meaning it comes with Salesforce and I don't see how to customize it. (Start and Stop Sync button)
If I were to update a record at the Quote Line Item object after syncing the Quote, the trigger works as expected.

Question:
How to execute a trigger on click of a Standard Button?

 

trigger UpdateOpportunityProductMonthsToFulfill on QuoteLineItem (after insert, after update) {
    String monthsToFulfill = '';
    map<String, String> updateMap = new map<String, String>();
    
    // This is where the text for the Months_To_Fulfill__c field is built
    for(QuoteLineItem qli : Trigger.new){
        // To make a spelled-out list of months & years instead of the older "start through end"
        
        List<String> yearOneMonths = qli.Months_Served_Y1__c.replaceAll('None(;)?', '').split(';');
        for(String month : yearOneMonths){
            monthsToFulfill += ( monthsToFulfill.length()==0 ? '' : '; ' ) + month + ' ' + qli.Year_Served_First__c;
        }
        if(!(qli.Year_Served_Second__c == null || qli.Year_Served_Second__c.equals('None'))){
            List<String> yearTwoMonths = qli.Months_Served_Y2__c.replaceAll('None(;)?', '').split(';');
            for(String month : yearTwoMonths){
                monthsToFulfill += '; ' + month + ' ' + qli.Year_Served_Second__c;
            }
        }
        
        updateMap.put(qli.QuoteId, monthsToFulfill);
    }
  
    for(String quoteID : updateMap.keySet()){
        // This is the calculated value we saved from looping through the Trigger.new Collection
        monthsToFulfill = updateMap.get(quoteID);
        
        // Get the OpportunityLineItems associated with this QuoteId
        List<OpportunityLineItem> oliList = [Select Id, OpportunityId, Months_To_Fulfill__c 
                                             From OpportunityLineItem Where OpportunityId In (Select OpportunityId 
                                                                                              From Quote Where Id = :quoteID )];
        
        List<OpportunityLineItem> olisToUpdate = new List<OpportunityLineItem>();
        
        if(oliList.size() > 0){
            for (OpportunityLineItem oli : oliList){
                // Populate this olisToUpdate list because we may 
                // want to get smarter about what's actually updated 
                // in the future.  For now, everything is updated.
                oli.Months_To_Fulfill__c = monthsToFulfill;
                olisToUpdate.add(oli);
            }
            
            // Batch update the OpportunityLineItems we identified earlier
            if(olisToUpdate.size() > 0){
                update olisToUpdate;
            }
        }
    }     
}
 

 

Background:
Our company works in print media and when we sell ads for our magazine, our reps fill the fulfillment information out at the Quote Line Item object.

Our accounting software can only read from the Opportunity Product level.

I have created a trigger that achieves that with one catch, it doesn't recognize when a rep needs to sync the primary quote with the Opportunity.

The button is a standard Salesforce button, meaning it comes with Salesforce and I don't see how to customize it. (Start and Stop Sync button)
If I were to update a record at the Quote Line Item object after syncing the Quote, the trigger works as expected.

Question:
How to execute a trigger on click of a Standard Button?

 

trigger UpdateOpportunityProductMonthsToFulfill on QuoteLineItem (after insert, after update) {
    String monthsToFulfill = '';
    map<String, String> updateMap = new map<String, String>();
    
    // This is where the text for the Months_To_Fulfill__c field is built
    for(QuoteLineItem qli : Trigger.new){
        // To make a spelled-out list of months & years instead of the older "start through end"
        
        List<String> yearOneMonths = qli.Months_Served_Y1__c.replaceAll('None(;)?', '').split(';');
        for(String month : yearOneMonths){
            monthsToFulfill += ( monthsToFulfill.length()==0 ? '' : '; ' ) + month + ' ' + qli.Year_Served_First__c;
        }
        if(!(qli.Year_Served_Second__c == null || qli.Year_Served_Second__c.equals('None'))){
            List<String> yearTwoMonths = qli.Months_Served_Y2__c.replaceAll('None(;)?', '').split(';');
            for(String month : yearTwoMonths){
                monthsToFulfill += '; ' + month + ' ' + qli.Year_Served_Second__c;
            }
        }
        
        updateMap.put(qli.QuoteId, monthsToFulfill);
    }
  
    for(String quoteID : updateMap.keySet()){
        // This is the calculated value we saved from looping through the Trigger.new Collection
        monthsToFulfill = updateMap.get(quoteID);
        
        // Get the OpportunityLineItems associated with this QuoteId
        List<OpportunityLineItem> oliList = [Select Id, OpportunityId, Months_To_Fulfill__c 
                                             From OpportunityLineItem Where OpportunityId In (Select OpportunityId 
                                                                                              From Quote Where Id = :quoteID )];
        
        List<OpportunityLineItem> olisToUpdate = new List<OpportunityLineItem>();
        
        if(oliList.size() > 0){
            for (OpportunityLineItem oli : oliList){
                // Populate this olisToUpdate list because we may 
                // want to get smarter about what's actually updated 
                // in the future.  For now, everything is updated.
                oli.Months_To_Fulfill__c = monthsToFulfill;
                olisToUpdate.add(oli);
            }
            
            // Batch update the OpportunityLineItems we identified earlier
            if(olisToUpdate.size() > 0){
                update olisToUpdate;
            }
        }
    }     
}