1

I currently have a trigger that has me rather confused.

You see, this trigger works in the Sandbox, passes tests in the Sandbox, passes test in Production, but does not work in Production. In other words, the field is not updated.

Here is the code:

My Support Classes

public with sharing class M_OpportunityService 
{
    public void AssignOpportunityCorrectBrands(Set<Id> opportunityIds)
    {
        Map<Id, Set<String>> opportunityToBrandMap = new Map<Id, Set<String>>();

        for(OpportunityLineItem singleLineItem : [SELECT OpportunityId, Brand__c FROM OpportunityLineItem WHERE OpportunityId IN :opportunityIds])
        {
            if(!opportunityToBrandMap.containsKey(singleLineItem.OpportunityId))
                opportunityToBrandMap.put(singleLineItem.OpportunityId, new Set<String>());
            opportunityToBrandMap.get(singleLineItem.OpportunityId).add(singleLineItem.Brand__c);
        }

        List<Opportunity> opportunitiesToBrand = [SELECT Id FROM Opportunity WHERE Id IN :opportunityIds];

        for(Opportunity singleOpportunity : opportunitiesToBrand)
            singleOpportunity.mkto71_Brand__c = String.join(new List<String>(opportunityToBrandMap.get(singleOpportunity.Id)), ';');

        UPDATE opportunitiesToBrand;
    }
}

public with sharing class M_OpportunityLineItemTriggerHandler 
{
    public static void UpdateOpportunityBrands(List<OpportunityLineItem> lineItems)
    {
        Set<Id> opportunityIds = new Set<Id>();

        for(OpportunityLineItem singleLineItem : lineItems)
            opportunityIds.add(singleLineItem.OpportunityId);

        (new M_OpportunityService()).AssignOpportunityCorrectBrands(opportunityIds);
    }
}

The Trigger Code

trigger M_OpportunityLineItemTrigger on OpportunityLineItem (after insert, after delete, after update) 
{
    if(Trigger.isInsert && Trigger.isAfter)
        M_OpportunityLineItemTriggerHandler.UpdateOpportunityBrands(Trigger.New);
    if(Trigger.isDelete && Trigger.isAfter)
        M_OpportunityLineItemTriggerHandler.UpdateOpportunityBrands(Trigger.Old);
    if(Trigger.isUpdate && Trigger.isAfter)
        M_OpportunityLineItemTriggerHandler.UpdateOpportunityBrands(Trigger.New);
}

The code simply takes picklist values from the Opportunity Product and populates the Opportunity field.

Now it would be different if the test code failed in Production, but it doesn't. Is there any possibility on what the issue is?

Here is the test code:

@isTest
private class M_OpportunityLineItemTrigger_CTest 
{
    private static testMethod void OpportunityLineItemTrigger_InsertTest()
    {
        Exception failureDuringExecution = null;

        Account testAccount = new Account(Name = 'Test Account 1');
        INSERT testAccount;
        Opportunity testOpportunity = new Opportunity(Name = 'Test Opportunity 1', AccountId = testAccount.Id, Type = 'Brand Delivery',
            LeadSource = 'Linkedin', CloseDate = System.now().date(), StageName = '95-Lost', Budget__c = 'Yes');
        INSERT testOpportunity;
        Product2 testProduct = new Product2(Name = 'Test Product 1');
        INSERT testProduct;
        PricebookEntry testPricebookEntry = new PriceBookEntry(Product2Id = testProduct.Id, PriceBook2Id = Test.getStandardPricebookid(),
            UnitPrice = 444.0, IsActive = true);
        INSERT testPricebookEntry;

        Test.startTest();

        try 
        { 
            INSERT new OpportunityLineItem(OpportunityId = testOpportunity.Id, Brand__c = 'Brandimage', Quantity = 10,  UnitPrice = 123,
                PricebookEntryId = testPricebookEntry.Id); 
        }
        catch(Exception failure) { failureDuringExecution = failure;  }

        Test.stopTest();

        Opportunity evaluatingOpportunity = [SELECT mkto71_Brand__c FROM Opportunity WHERE Id = :testOpportunity.Id];

        System.assertEquals('Brandimage', evaluatingOpportunity.mkto71_Brand__c, 'The brand insert failed!');
        System.assertEquals(null, failureDuringExecution, 'There was an exception thrown during the test!');
    }

    private static testMethod void OpportunityLineItemTrigger_DeleteTest()
    {
        Exception failureDuringExecution = null;

        Account testAccount = new Account(Name = 'Test Account 1');
        INSERT testAccount;
        Opportunity testOpportunity = new Opportunity(Name = 'Test Opportunity 1', AccountId = testAccount.Id, Type = 'Brand Delivery',
            LeadSource = 'Linkedin', CloseDate = System.now().date(), StageName = '95-Lost', Budget__c = 'Yes');
        INSERT testOpportunity;
        Product2 testProduct = new Product2(Name = 'Test Product 1');
        INSERT testProduct;
        PricebookEntry testPricebookEntry = new PriceBookEntry(Product2Id = testProduct.Id, PriceBook2Id = Test.getStandardPricebookid(),
            UnitPrice = 444.0, IsActive = true);
        INSERT testPricebookEntry;
        OpportunityLineItem testOpportunityLineItem = new OpportunityLineItem(OpportunityId = testOpportunity.Id, Brand__c = 'Brandimage', 
            Quantity = 10,  UnitPrice = 123, PricebookEntryId = testPricebookEntry.Id);
        INSERT testOpportunityLineItem;

        Test.startTest();

        try { DELETE testOpportunityLineItem; }
        catch(Exception failure) { failureDuringExecution = failure;  }

        Test.stopTest();

        Opportunity evaluatingOpportunity = [SELECT mkto71_Brand__c FROM Opportunity WHERE Id = :testOpportunity.Id];

        System.assertEquals(null, evaluatingOpportunity.mkto71_Brand__c, 'The brand delete failed!');
        System.assertEquals(null, failureDuringExecution, 'There was an exception thrown during the test!');
    }

    private static testMethod void OpportunityLineItemTrigger_UpdateTest()
    {
        Exception failureDuringExecution = null;

        Account testAccount = new Account(Name = 'Test Account 1');
        INSERT testAccount;
        Opportunity testOpportunity = new Opportunity(Name = 'Test Opportunity 1', AccountId = testAccount.Id, Type = 'Brand Delivery',
            LeadSource = 'Linkedin', CloseDate = System.now().date(), StageName = '95-Lost', Budget__c = 'Yes');
        INSERT testOpportunity;
        Product2 testProduct = new Product2(Name = 'Test Product 1');
        INSERT testProduct;
        PricebookEntry testPricebookEntry = new PriceBookEntry(Product2Id = testProduct.Id, PriceBook2Id = Test.getStandardPricebookid(),
            UnitPrice = 444.0, IsActive = true);
        INSERT testPricebookEntry;
        OpportunityLineItem testOpportunityLineItem = new OpportunityLineItem(OpportunityId = testOpportunity.Id, Brand__c = 'Brandimage', 
            Quantity = 10,  UnitPrice = 123, PricebookEntryId = testPricebookEntry.Id);
        INSERT testOpportunityLineItem;

        testOpportunityLineItem.Brand__c = 'Anthem';

        Test.startTest();

        try { UPDATE testOpportunityLineItem; }
        catch(Exception failure) { failureDuringExecution = failure;  }

        Test.stopTest();

        Opportunity evaluatingOpportunity = [SELECT mkto71_Brand__c FROM Opportunity WHERE Id = :testOpportunity.Id];

        System.assertEquals('Anthem', evaluatingOpportunity.mkto71_Brand__c, 'The brand update failed!');
        System.assertEquals(null, failureDuringExecution, 'There was an exception thrown during the test!');
    }
}
3
  • 3
    Can you edit your post and clarify what you mean when you say the trigger isn't working? Mar 10 '17 at 20:42
  • I added some clarification. If you need more, let me know. Mar 10 '17 at 20:45
  • a test will execute with the running user's permissions - most likely a sysad; whereas live, the code runs with the user's permissions. are these the same?
    – cropredy
    Mar 10 '17 at 23:23
2

I suggest you review the debug logs to get an idea of exactly what is going on.

I took your code and removed everything except the core logic, added the fields to the Opportunity and Line Item (naming is different) then created a trigger for after update. (I made some small modification that were just a different way of doing things but accomplished the same goal)

I have confirmed this works. If it does not for you then check sharing etc. If it works start adding back in your other framework to find where it breaks....

This will at least give you a sanity check...

trigger testMe on OpportunityLineItem (after update) {
        Map<Id, Set<String>> opportunityToBrandMap = new Map<Id, Set<String>>();

        for(opportunityLineItem oli : trigger.new){
            if(!opportunityToBrandMap.containsKey(oli.OpportunityId)) opportunityToBrandMap.put(oli.OpportunityId,new Set<String>());
        }

        for(OpportunityLineItem singleLineItem : [SELECT OpportunityId, BrandPL__c FROM OpportunityLineItem WHERE OpportunityId IN :OpportunityToBrandMap.keySet()])
        {
            if(!string.isBlank(singleLineItem.BrandPL__c)) opportunityToBrandMap.get(singleLineItem.OpportunityId).add(singleLineItem.BrandPL__c);
        }

        List<Opportunity> opportunitiesToBrand = New Opportunity[]{};

        for(Id singleOpportunityId : opportunityToBrandMap.keySet())
            opportunitiesToBrand.add(
                New Opportunity(
                    Id = singleOpportunityId,
                    BrandField__c = String.join(new List<String>(opportunityToBrandMap.get(singleOpportunityId)), ';')
                )
           );

        UPDATE opportunitiesToBrand;

}
1

I just created a class that basically does the same thing last week. When I compare my code that works in prod to yours we're the same till you get to your second query

 List<Opportunity> opportunitiesToBrand = [SELECT Id FROM Opportunity WHERE Id IN :opportunityIds];

    for(Opportunity singleOpportunity : opportunitiesToBrand)
        singleOpportunity.mkto71_Brand__c = String.join(new List<String>(opportunityToBrandMap.get(singleOpportunity.Id)), ';');

I don't think you need to do this and thus could be the cause.

Along with putting some system.debug() statements before the update to see if you actually have a record to update try this:

Opportunity[] oppyU = new Opportunity[]{};
for(Id oppId : opportunityToBrandMap.keySet())
{
    oppyU.add(
    New Opportunity(
        Id = oppId,
        mkto71_Brand__c = String.join(new List<String>(opportunityToBrandMap.get(oppId)), ';'); 
}
update opportunitiesToBrand;
2
  • I tried it and it still didn't work :( . Mar 10 '17 at 21:52
  • does debug say that there are values in your lists? Mar 10 '17 at 22:05
0

I finally found the solution to my problem once I pushed a debug statement to my Production environment.

It was a visibility issue.

It wasn't a visibility issue on the profile level, but it was hidden due to the field not being on the Opportunity Product layout. Once I added that field to the layout, the values rolled-up properly to the parent object.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.