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
Phil Westerby-JonesPhil Westerby-Jones 

Quote and Quote Line item Price Book mismatch Problems in Apex Test Class, only in Production instance

I'm getting this error "LB_TestProductExplosion.TestOpportunityWithTriggerProduct(), Details: System.DmlException: Insert failed. First exception on row 0; first error: FIELD_INTEGRITY_EXCEPTION, The pricebook entry is in a different pricebook than the one assigned to the Quote, or Quote has no pricebook assigned.: [PricebookEntryId] " when running my tests in the Operational Instance.

It works fine in my Dev and in The Sandbox of The Operational Instance.

I've burned over 6 hours on this now and have tried various changes/rewrites in my Apex code, which have basically made no difference. I've used Debug to display the Product, PB and PBE details step by step and all is fine in Dev/Sandbox, but on doing or testing the deployment in The Operational Environment, it's no go. I've searched the forums and this seems to be a known problem, but other than the real basic stuff answers, there's no real solution, which is unusual for SF problems in my experience.

VERY frustrating!! HELP!!!!
Best Answer chosen by Phil Westerby-Jones
Phil Westerby-JonesPhil Westerby-Jones
This is now fixed HOORAY!!!!!!!

The diagnosois was significantly hampered until I finally found out that if I opened the Developer Console in Production BEFORE running the Validate/Deploy, I'd get the usual debug logs.......... so I was working in the dark before and just on SandBox logs #NewbieError

I've had to use (SeeAllData=true) from day one on all my tests which include Price Books, as I've never been able to set up a standard Price Book via Apex and therefore had to use the System Standard Price Book. How DO I setup a Standard Price Book in Apex by the way? :) Anyway this also meant that the other system data was visible......... On checking the Production Log, the Quote PBID was Different to the Opp PBID and on closer investigation....... guess what, there was already an existing Quote called "Test Quote"! Renamed it to "Test Quote 1" and all passed!!

Problem solved and a NUMBER of new things learned which hopefully others will learn from! Thanks for your help Venkat!!

 

All Answers

Venkat PolisettiVenkat Polisetti
Phil,

In your test method, make sure you assign the pricebook2Id field a value. Normally, when you create a Quote from the UI, Quote get the price book id from Opportunity. But in a Test environment, you need to set it.

Venkat
Phil Westerby-JonesPhil Westerby-Jones
Thanks Verkat. I'm getting the PBook ID from the Opportunity and writing it to the quote object, so I think I'm setting it, or do you mean something more than this? It works fine in Dev and Sandbox, just not in final Production test!!! *Phil Westerby-Jones* Principal Business Architect 07720 294494 [image: Little Blue Logo] [image: Website Link] [image: Skype Link] [image: Twitter Link] [image: Zoho Logo] [image: Salesforce Logo] [image: BiY Awards Shortlisted - Rockstars in Customer Service] ​
Venkat PolisettiVenkat Polisetti
Phil,

You may want post your test case code here so that we can see what is going on, if that is alright with you.

Thanks,
Venkat
Phil Westerby-JonesPhil Westerby-Jones
That's a great idea Venkat!! This is the section of code that creates the Opp Line item and the quote and Quote Line Item. The quote stuff was in another area, but I pulled it all together in the debugging/rewrite work today. The Opportunity and Product share the same name.

As I've said, this works 100% in dev and sandbox, but not in Production!! I've checked the objects in Production and there's nothing obviously different and no more mandatory fields etc? 


private static OpportunityLineItem createLineItem(String prodName) {

                        //Get the Opportunity
                        Opportunity o = [ SELECT ID, Pricebook2ID, Name
                                    FROM Opportunity
                                    WHERE Name=:prodName                             
                                    LIMIT 1
                           ];

        system.debug('ProdOpp Name/o.ID:' + o.Name + '/' + o.Id);
        system.debug('PriceBook2ID:' + o.Pricebook2ID);
                        
                        //Get the Product
                        Product2 prod1 = [ SELECT id
                            FROM Product2
                            WHERE Name=:prodName                             
                            LIMIT 1
                           ];
        
                        //Get Price Book Entry ID
                        PricebookEntry getpb = [ SELECT id
                             FROM PricebookEntry
                             WHERE Product2ID=:prod1.ID
                             LIMIT 1
                           ];    
        
                    OpportunityLineItem line = new OpportunityLineItem(
                                        OpportunityID = o.ID,
                                        PricebookEntryID = getpb.id,
                                        UnitPrice = 123.45,
                                        Quantity = 3);  
                    Insert line;
        
                    //Get the Opportunity again
                    Opportunity o1 = [ SELECT ID, Pricebook2ID, Name
                                    FROM Opportunity
                                    WHERE Name=:prodName                             
                                    LIMIT 1
                       ];

        system.debug('ProdOpp Name/o1.ID:' + o1.Name + '/' + o1.Id);
        system.debug('PriceBook2ID:' + o1.Pricebook2ID);
        
                    Quote q = new Quote(
                                OpportunityID = o1.Id,
                                Name = 'Test Quote',
                                PriceBook2ID = o1.Pricebook2ID);  
                    Insert q;
        
system.debug('Quote Q:' + q);
        
                    //Get the Quote
                    Quote thisQuote = [ SELECT id, PriceBook2ID 
                            FROM Quote
                            WHERE Name='Test Quote'
                            LIMIT 1
                    ];
        
system.debug('Quote thisQuote:' + thisQuote);

                    QuoteLineItem qLine = new QuoteLineItem(
                            QuoteID = thisQuote.id,
                            PricebookEntryID = getpb.id,
                            UnitPrice = 123.45,
                            Quantity = 3);
                    Insert qLine;
                    
system.debug('QuoteLI qLine:' + qLine); 
        
    Return line;
    }
Venkat PolisettiVenkat Polisetti
Phil,

I do not see any issues with the code. One thing though, before you call this method, Opportunity is already created. Make sure that you are assigning Pricebook when you create your Opportunity.

Venkat
Phil Westerby-JonesPhil Westerby-Jones
Thanks for the feedback man :)

I'm not specifically assigning the Price Book ID to the opportunity, but I'll try that and let you know.

Thank you for your help and feedback man, much appreciated!! P
Phil Westerby-JonesPhil Westerby-Jones
OK, I added the PB ID to the Opportunity on creation, through moving things around and there WAS a difference in the PB IDs when I compared them when adding the Opp Line item, one of them was using the Standard PB and the other the custom, so I changed the SELECT to include the PD ID,  thanks for that Venkat! The test code ran beautifully in sandbox.............. BUT...... I've still got exactly the same problem when deploying to production :(

I've put in even more debugs and the PB ID and PBE IDs match up, from Opp to Quote and both sets of line items, but SOMETHING isn't right when going to Production :( I'm really happy to blame myself, but this is looking like something wrong with the platform? - How can it pass in Sandbox, but not in Production?!!
Venkat PolisettiVenkat Polisetti
I believe, you may have to post the full code so that we can see what is going on. If you post code, please use the "Add Code Sample" button in the editor (< >) that is in the pallette.
Phil Westerby-JonesPhil Westerby-Jones
Hi Venkat, here is the entire code

This is the calling class:
 
@isTest (SeeAllData=true)
private class LB_TestProductExplosion {

static testmethod void TestOpportunityWithTriggerProduct() {

    //Create the Template Opportunity
    Opportunity testTemplate = LB_TestFactoryProductExplosion.createOpportunity(True, 'TestProduct1', 'TestAccount1', 'TestContact1', True, 'Test Quote');
    
system.debug('We are getting to this point, 1');

    Try {	
        
        Test.startTest();
        
        // Create an Opportunity with a trigger product
     	Opportunity testOpportunity = LB_TestFactoryProductExplosion.createOpportunity(False, 'TestProduct1', 'TestAccount2', 'TestContact2', True, 'Test Quote');

system.debug('We are getting to this point, 2, so it is obviously creating the 2nd opportunity');

        Opportunity testOpp = [ SELECT ID, Name, StageName FROM Opportunity 
        							WHERE Name = 'TestProduct1'
        							AND StageName = 'TestStage'
                               		LIMIT 1
                              ];
        
system.debug('LB_TestProductExplosion/Opportunity Name: ' + testOpp.Name);
        
    	OpportunityLineItem testLI = [ SELECT ID, OpportunityID FROM OpportunityLineItem 
        							WHERE OpportunityID =:testOpp.ID 
                                    LIMIT 1
                              ];
        
system.debug('LB_TestProductExplosion/testLI=' + testLI);
system.debug('LB_TestProductExplosion/Opportunity ID ' + testOpp.ID + ' Opp ID from LI ' + testLI.OpportunityID);

        Test.stopTest();
            
       	System.assertEquals(testOpp.ID, testLI.OpportunityID);
       	}
    Catch (Exception error) {
        system.debug('LB_TestProductExplosion: ' + error.getmessage() + ' Error Line:' + error.getlinenumber());
	}        
}
}

and here is the factory class:
 
@isTest (SeeAllData=true)
public class LB_TestFactoryProductExplosion {

    
    public static Opportunity createOpportunity(Boolean withProduct, String productName, String accName, String conName, Boolean createQuote, String quoteName) {
		
        Boolean template = False;
        If (withProduct == true) {
    				template = True;
		}
        
        If (withProduct == true) {
			// Create Test Product
			Product2 prod = createProduct(productName);          
            
            // Create Test Pricebook
			Pricebook2 lBTestPriceBook = createPricebook();
			
            // Associate product with The Pricebook
			Pricebookentry p = AddProduct('LittleBlueTestPriceBook', productName);
        }
       
        Opportunity lBTestOpportunity = AddOpportunity(productName, accName, conName, template);
        
        If (withProduct == true) {
			
            // Add the product to the Opportunity
            OpportunityLineItem l = createLineItem(productName);                                          
		
        	// Maybe create a quote from the opportunity 29102014
            If (createQuote == True) {
system.debug('createQuote IS True!!');
            	// Commented the whole section out 311014 as big problems with
            	// Quote/Quote Line items on deploying to Production
                // Quote qq = createQuote(productName, quoteName);   
            }
        }
		return lBTestOpportunity;
	}
    

    // Helper methods
    
    
    private static OpportunityLineItem createLineItem(String prodName) {

        				//Get the Opportunity
        				Opportunity o = [ SELECT ID, Pricebook2ID, Name
            						FROM Opportunity
                    				WHERE Name=:prodName                             
                    				LIMIT 1
           				];

system.debug('ProdOpp Name/o.ID:' + o.Name + '/' + o.Id);
system.debug('Opp PriceBookID:' + o.Pricebook2ID);
        				
        				//Get the Product
        				Product2 prod1 = [ SELECT id
            				FROM Product2
                    		WHERE Name=:prodName
                    		LIMIT 1
           				];
        
        				//Get Custom Price Book Entry ID
        				PricebookEntry getpb = [ SELECT id, PriceBook2ID
                     		FROM PricebookEntry
                     		WHERE Product2ID=:prod1.ID
                            AND PriceBook2ID=:o.Pricebook2Id
                     		LIMIT 1
               			];	
        
system.debug('PBE PriceBookID:' + getpb.Pricebook2Id);
system.debug('PBE ID:' + getpb.id);
        
        			OpportunityLineItem line = new OpportunityLineItem(
                    					OpportunityID = o.ID,
                        				PricebookEntryID = getpb.id,
                        				UnitPrice = 123.45,
                        				Quantity = 3);  
    				Insert line;
        
system.debug('Opp LItem PBE ID:' + getpb.id);
        
        			//Get the Opportunity again
        			Opportunity o1 = [ SELECT ID, Pricebook2ID, Name
            						FROM Opportunity
                    				WHERE Name=:prodName                             
                    				LIMIT 1
           			];

system.debug('ProdOpp Name/o1.ID:' + o1.Name + '/' + o1.Id);
system.debug('OPP PriceBookID:' + o1.Pricebook2ID);
        
                	Quote q = new Quote(
                    			OpportunityID = o1.Id,
                        		Name = 'Test Quote',
                        		PriceBook2ID = o1.Pricebook2Id);  
    				Insert q;
        
system.debug('Quote Q:' + q);
        
        			//Get the Quote
        			Quote thisQuote = [ SELECT id, PriceBook2ID 
                            FROM Quote
                            WHERE Name='Test Quote'
                            LIMIT 1
                    ];
        
system.debug('Quote thisQuote:' + thisQuote);

        			QuoteLineItem qLine = new QuoteLineItem(
                    		QuoteID = thisQuote.id,
                        	PricebookEntryID = getpb.id,
                        	UnitPrice = 123.45,
                        	Quantity = 3);
        			Insert qLine;
        			// The above insert is where the problem's happening
        			// on deployment to Production 311014
        			
system.debug('QuoteLI qLine:' + qLine); 
        
    Return line;
    }
                            
 	//Create an Opportunity
    private static Opportunity AddOpportunity(String oppName, String aName, String cName, Boolean template) {

        				//Add the Test Account
        				Account a1 = new Account(Name=aName);    					
    					insert a1;
        
        				//Get the Test Account
        				Account a = [ SELECT ID
            						FROM Account
                    				WHERE Name=:aName                             
                    				LIMIT 1
           				];
        
        				//Add the Test Contact
        				Contact c1 = new Contact(LastName=cName, AccountID=a.ID);
        				insert c1;
        
        				//Get the Test Contact
        				Contact c = [ SELECT ID
            						FROM Contact
                    				WHERE LastName=:cName                             
                    				LIMIT 1
           				];
        
        		//Get the Pricebook
        		Pricebook2 testpb1 = [ SELECT id
                     FROM Pricebook2
                     WHERE Name ='LittleBlueTestPriceBook'
                     LIMIT 1
               ];
        
        //Create the opportunity	
        //PWJ 29.7.2014 added the code to add a null search product if
        //this isn't a template, as the search product in the template was 
        //causing a loop on adding the template opportunity and the problem 
        //I've been worrying about since early this morning :)
        String stage = 'TestStage';
        String searchProduct = oppName;
        String notSearchProduct = 'NotASearchProduct';
        If (template == True) {
            stage = 'ExplosionTemplate';
            searchProduct = '';
            notSearchProduct = '';
        }
        
        // Specifically added PB ID 311014
        Opportunity opp = new Opportunity(
                    			AccountID = a.ID, 
            					Contact__c = c.ID,
                    			CloseDate = System.Today() + 90, 
                    			Name = oppName,
            					Amount = 3*123.45,
            					PriceBook2ID = testpb1.id,
            					Consumables__c = searchProduct,
            					Media__c = notSearchProduct,
            					Software_Modules__c = searchProduct,
                    			StageName = stage); 
system.debug('LB_TestFactoryProductExplosion/Opportunity:' + opp) ;               	   					

    insert opp;
    return opp;
	}

	private static Product2 createProduct(String prodName) {
		Product2 p = new Product2(Name=prodName);
		insert p;

system.debug('Product created');

    return p;
	}
        
    //Create the PriceBook
    private static Pricebook2 createPricebook() {        
    		Pricebook2 pBook = new Pricebook2(Name='LittleBlueTestPriceBook', IsActive = True);
	insert pBook;
        
system.debug('Pricebook created');

        return pBook;
	}
 	    
       private static PricebookEntry AddProduct(String priceBookName, String product) {
       PricebookEntry pBook = new PricebookEntry();
           String test = product;
           Pricebook2 spb = [  SELECT id 
                  FROM Pricebook2
                  WHERE isStandard=true
                  LIMIT 1
               ];
           
           // Tried this from Summer 2014 functionality 311014 but not recognised,
           // so looks like Summer's not here yet
           // Id spricebookId = Test.getStandardPricebookId();

           Pricebook2 testpb = [ SELECT id
                     FROM Pricebook2
                     WHERE Name =:priceBookName
                     LIMIT 1
               ];
           
         	Product2 prod = [ SELECT id
            		FROM Product2
                    WHERE Name=:product                             
                    LIMIT 1
           		];
           
           	PricebookEntry spbe = new PricebookEntry(
           					Pricebook2ID = spb.id,
           					IsActive = True,
           					Product2ID = prod.ID,
           					UnitPrice = 123.45);
	insert spbe;
           
           PricebookEntry tpbe = new PricebookEntry(
           					Pricebook2ID = testpb.id,
           					IsActive = True,
           					Product2ID = prod.ID,
           					UnitPrice = 543.21);
	insert tpbe;      
	return tpbe;
	}
    
    // This whole section is commented out frm being called at the moment 311014
    private static Quote createQuote(String prodName, String quoteName) {

system.debug('Creating the Quote: ' + quoteName);
        
        			//Get the Opportunity
        			Opportunity o = [ SELECT ID, Pricebook2Id
            					FROM Opportunity
                    			WHERE Name=:prodName                             
                    			LIMIT 1
           			];
        
system.debug('Opportunity PBID:' + o.PriceBook2ID);
        				        				        				
        			//Get the Product
        			Product2 prod1 = [ SELECT id
            				FROM Product2
                    		WHERE Name=:prodName                             
                    		LIMIT 1
           			];
        
        			//Get Price Book Entry ID ToDo get it selecting the correct price book 23072014
        			PricebookEntry getpb = [ SELECT id, Pricebook2ID, Product2ID
                     		FROM PricebookEntry
                     		WHERE Product2ID=:prod1.ID
                            AND Pricebook2ID=:o.Pricebook2Id
                     		LIMIT 1
               		];
        
system.debug('PBEntry getpb:' + getpb);
//system.debug('Opp Product ID:' + getpb.Product2ID);
        
                	system.debug('Got the opportunity: ' + prodName);
        			Quote q = new Quote(
                    				OpportunityID = o.ID,
                        			Name = quoteName,
                        			PriceBook2ID = o.Pricebook2Id
                        				);  
    				Insert q;
        
system.debug('Quote Q:' + q);
        
        			//Get the Quote
        			Quote thisQuote = [ SELECT id, PriceBook2ID 
                            FROM Quote
                            WHERE Name=:quoteName
                            LIMIT 1
                    ];
        
system.debug('Quote thisQuote:' + thisQuote);

        			QuoteLineItem qLine = new QuoteLineItem(
                    		QuoteID = thisQuote.ID,
                        	PricebookEntryID = o.Pricebook2Id,
                        	UnitPrice = 123.45,
                        	Quantity = 3);
        			Insert qLine;
        			
system.debug('QuoteLI qLine:' + qLine);        
        
    Return q;
    }
   
}

Good luck!! :)
Phil Westerby-JonesPhil Westerby-Jones
This is now fixed HOORAY!!!!!!!

The diagnosois was significantly hampered until I finally found out that if I opened the Developer Console in Production BEFORE running the Validate/Deploy, I'd get the usual debug logs.......... so I was working in the dark before and just on SandBox logs #NewbieError

I've had to use (SeeAllData=true) from day one on all my tests which include Price Books, as I've never been able to set up a standard Price Book via Apex and therefore had to use the System Standard Price Book. How DO I setup a Standard Price Book in Apex by the way? :) Anyway this also meant that the other system data was visible......... On checking the Production Log, the Quote PBID was Different to the Opp PBID and on closer investigation....... guess what, there was already an existing Quote called "Test Quote"! Renamed it to "Test Quote 1" and all passed!!

Problem solved and a NUMBER of new things learned which hopefully others will learn from! Thanks for your help Venkat!!

 
This was selected as the best answer