0

Our admins developed and activated a process builder in Production to set a default custom pricebook on opportunity during the opportunity creation process. Due to this, several of our test classes are failing. In our test classes, we are inserting products with the name "Test Product 1" etc and creating opportunity using these products. However, the test classes are failing with the following error message.

System.DmlException: Insert failed. First exception on row 0; first error: 
FIELD_INTEGRITY_EXCEPTION, field integrity exception: unknown (pricebook 
entry is in a different pricebook than the one assigned to the opportunity): 
[unknown] 

Stack Trace: Class.TestOpportunity1.testMethod_Execute: line 115, column 1

It appears that the Opportunity in test class is associated with the Standard pricebook and the process builder is trying to associate the Opportunity with the custom default pricebook and hence it fails.

In the test class, if I change the pricebookEntryId on the Opportunity to the custom pricebook, I get a different message saying the Product is not defined in Standard Pricebook.

Any solution?

Below is the sample code.

    Pricebook2  Pb = [select id, name, isActive from Pricebook2 where IsStandard = true limit 1];

    Product_Family__c pf = new Product_Family__c(Name = 'Collateral Protection',Product_Family__c='Collateral Protection');
    insert pf;

    Product2 p = new Product2 (Name='Test Product Entry 1',Product_Family__c=pf.Id,Description='Test Product Entry 1',productCode = '501', isActive = true);
    insert p;

    Campaign_Product__c cp = new Campaign_Product__c();
    cp.Campaign__c = c.Id;
    cp.Product__c = p.Id;
    insert cp;

    PricebookEntry pbe1 = new PricebookEntry (Product2ID=p.id,Pricebook2ID=Pb.id,UnitPrice=50, isActive=true);
    insert pbe1;

    Opportunity o1 = new Opportunity(AccountId = a.Id, Name = 'TEST', StageName = '1 - Discover', CloseDate = Date.today(), DoNotPursue__c=false, Take_Action_Date__c = Date.today());
    o1.Product__c =p.id;
    o1.Deal_Create_Date__c = date.today();
    o1.RecordTypeId = [SELECT Id FROM RecordType WHERE Name = 'Deal' AND IsActive=true limit 1].Id ;
    o1.CampaignId = c.Id;
    insert o1;

    OpportunityLineItem oli = new OpportunityLineItem (OpportunityID=o1.id, Expected_Implementation_Date__c = Date.today(),PricebookEntryId=pbe1.id);
    oli.PricebookEntryId=pbe1.id; 
    insert oli;

    string query = 'o1.Id'

    CampaignBatch cb = new CampaignBatch(query);

    Test.startTest();   
    Database.executeBatch(cb,200);

Test.stopTest();

EDIT: Thanks for the responses.

I already tried inserting the standard pricebook and in that case I am getting a flow error with the message "An unhandled flow error has occured" or something like that. We have a flow at the Opportunity Product level which assigns the Product Name to a "Product Selected" field when the Product2Id on the Opportunity Product is not null. I get an email about the flow exception, but there is no useful information in the email. Below is the contents of the email.

Subject: Sandbox: Error Occurred During Flow "Populate_Product_Selected": 
The flow failed to access the value for myVariable...

Error element myDecision (FlowDecision).
The flow failed to access the value for myVariable_current.Product2.Id 
because it hasn't been set or assigned.

This report lists the elements that the flow interview executed. The 
report is a beta feature.
We welcome your feedback on IdeaExchange.

Flow Details
Flow Name: Populate_Product_Selected
Type: Record Change Process
Version: 4
Status: Active

Flow Interview Details
Interview Label: 
Current User: <Current User> (005xyaxz)
Start time: 2/22/2018 2:50 PM
Duration: 0 seconds

How the Interview Started
<current user> (005xyaxz) started the flow interview.
Some of this flow's variables were set when the interview started.
myVariable_old = null
myVariable_current = 00k4FXYALKDSDxa

ASSIGNMENT: myVariable_waitStartTimeAssignment
{!myVariable_waitStartTimeVariable} Equals {!Flow.CurrentDateTime}
Result
{!myVariable_waitStartTimeVariable} = "2/22/2018 2:50 PM"

Salesforce Error ID: 28133274-19899 (540950916)

Below is the screenshot of the Process Builder Criteria.

Process Builder Criteria

I even looked at the debug logs during the flow failure and all I could see is the message "An unhandled flow event has occurred". It appears that the flow is not finding the value for Product2Id during the Opportunity Product insert from the test class.

Any thoughts?

EDIT: Finally I ended up making a change to the trigger instead of process builder. It appears that the process builder flow seems to be firing before even the insert opportunitylineitem statement completes the execution. As a result, when the flow executes, the Product2 field on the opportunity product seems to be null and hence the flow fails the above message. Again, this is just a theory. I tried very hard to get rid of this error message but to no avail and I had to change my design from Process builder to trigger.

  • 1
    Please include an example of your test code if you desire any assistance with it. – Adrian Larson Feb 23 '18 at 3:44
  • If you create the Opportunity first can you see its Pricebook2Id? Can you use that value instead for your PricebookEntry? – Adrian Larson Feb 23 '18 at 4:02
2

You need to create the standard price book entry for the product, first, then create the price book entry for the custom price book.

PricebookEntry[] pbe1 = new PricebookEntry[] {
  new PricebookEntry (Product2ID=p.id,Pricebook2ID=Test.getStandardPricebookId(),UnitPrice=50, isActive=true),
  new PricebookEntry (Product2ID=p.id,Pricebook2ID=Pb.id,UnitPrice=50, isActive=true) };
insert pbe1;

Note here that pb.id should be the custom price book. You get the standard price book Id "for free" when you use Test.getStandardPricebookId(), saving you a query.

0

Honestly, if you want your unit tests to be durable against Process Builders, you need to be able to turn the Process Builders off or control their behaviour. You can put a "kill switch" at the top of the Process Builder that stops the whole thing if some particular condition is true, e.g. a custom setting that you only set in a Test context, or the presence of a particularly unlikely string in the Opportunity name ("BypassFlowPlease"), etc.

Whether or not your PBs should be active for various unit tests is hotly debatable. On one hand you want them to represent the way your org actually operates. On the other hand you want to be able to test individual components in a decoupled manner.

Question: You wrote "Our admins developed and activated a process builder in Production to set a default custom Price Book on opportunity during the opportunity creation process." However you have shown us a Process that runs on Opportunity Product, not Opportunity. How does this Process Builder on Opportunity reference that Price Book? If it's via a hard-coded ID, of course it's going to gum up unit tests where that ID doesn't exist. If it's via a Custom Setting, then your unit test can control that behaviour by inserting the Custom Setting first before doing any DML that will invoke Process Builder.

  • the root cause is Our admins developed and activated a process builder in Production and didn't go through development processes that would have caught this issue before deployment :-) – cropredy Feb 27 '18 at 2:50
  • Totally. And the reality is, in many business environments, the admins have authority to decide what should be in the Salesforce instances. The developer should escalate the issue to whoever has authority to resolve technical conflicts. – Charles T Feb 27 '18 at 4:26
  • @CharlesT, I do not want to over-complicate the test classes issue by adding a custom setting or something like that. It was easier for me to get rid of the process builder and do the same thing via the trigger. Regarding the PB, admins have developed the PB on opportunity that sets the pricebook to a custom pricebook (by hardcoding the pricebook Id). I had to fix the test classes by adding standard price book entry while inserting opportunity product in test classes. The PB on Opportunity product was created by me and that was causing other issues which was not easy to address. – Balaji Pooruli Feb 27 '18 at 4:31
  • Complication is a matter of degree. A PB that hardcodes an ID is bound to DEEPLY complicate Apex unit tests. Custom settings are a convenient way to avoid hard coding and give some flexibility to unit testing. – Charles T Feb 27 '18 at 4:48
  • I agree. However, our admins never used custom settings and we (developers) came to know about this process builder only when the test classes started failing. – Balaji Pooruli Mar 1 '18 at 0:29

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.