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
FlorentFlorent 

How to update a required formula field to !null before very first db insert? [unit test]

Hello all,

 

I'm striving to complete my very first deployment of a SF trigger but am stuck in the (very first!!) unit test.

 

Briefly said, I'm creating a fictive account in my test class, then an opportunity.

 

I'm "connecting" (sorry, bad english here) my opportunity to the account but when I test the class, and I insert the opp. in the database (which executes the trigger, right?) I have an error message I wrote myself, from a validation rule, saying that my account does not have an account_code (a required 2-letters code custom field), which is required to create the opportunity number (Opp. number consist of: account code + a 4-digit autonumbering).

 

I hope I'm being clear enough! here's a copy of some of the log's extracts:

 

Line 24 12:15:27.368 (1368862000)|USER_DEBUG|[19]|DEBUG|acc01 = Account:{Name=dsi, Account_type__c=CRO, MC_Account_code__c=AA, Id=001f0000002a3sbAAA}

Line 25 12:15:27.369 (1369440000)|USER_DEBUG|[41]|DEBUG|opp01= Opportunity:{Received_Date__c=2012-06-14 00:00:00, Name=sftest01, MC_Opportunity_filiations__c=PARENT, StageName=RFP Received, NextStep=no next step, Priority__c=Low, Group__c=XXX Consultancy, CloseDate=2012-08-13 00:00:00}

 

Interrestingly, opp01 has no value for the Account (opp01.Account), even though it is defined in my code before the system.debug calls.

 

I verify this explicitely, in the next line from the log

 

Line 26 12:15:27.369 (1369526000)|USER_DEBUG|[42]|DEBUG|opp01.Account.MC_Account_code__c = AA

 

So, it seems, opp01.Account is indeed linked to the account and I have access to its Account_code__c…

 

Just to be sure, I have just added a system.debug message which says:

 

14:16:58.199 (2199397000)|USER_DEBUG|[42]|DEBUG|opp01.Account = Account:{Name=dsi, Account_type__c=CRO, MC_Account_code__c=AA, Id=001f0000002a44XAAQ}

 

Before the execution of the trigger, there is a validation rule that tests the value of opp01.MC_Account_code to be different from null:

 

Line 762 12:15:27.434 (1434787000)|VALIDATION_RULE|03df0000000013E|MC_Check_Account_code
Line 763 12:15:27.435 (1435011000)|VALIDATION_FORMULA|IF( ISPICKVAL( Group__c, "XXX Consultancy"),
Line 764     IF( MC_Account_code__c  = "-1",
Line 765         TRUE,
Line 766         FALSE
Line 767     ),
Line 768     FALSE
Line 769)|Group__c=XXX Consultancy , MC_Account_code__c=-1
Line 770 12:15:27.435 (1435027000)|VALIDATION_FAIL
Line 771 12:15:27.435 (1435551000)|VALIDATION_RULE|03dD0000000MB3g|Project_Type_cannot_be_set_for_this_Opp

 

Trouble is, the field MC_Account_code for the opportunity object is a formula saying:

 

IF(ISBLANK(Account.MC_Account_code__c), "-1", Account.MC_Account_code__c)

 

So it would seem that Account.MC_Account_code__c is "blank" (null indeed). But at the beggining of the test, we didi see the value of Account.MC_Account_code__c for the account acc01, was… AA ! (see ligne 24).

 

So. I don't understand!

 

I have read on multiple occasions that one should update the formula using a SOQL select. Right, ok, fair enough, but how does one do this when this is the very first opportunity created in the (test) database?!

 

How do I update a non existent record so that the required formula field (for its creation) is !null ?!!

Best Answer chosen by Admin (Salesforce Developers) 
sfdcfoxsfdcfox

Assign acc01.Id to opp01.AccountId instead. You're assigning the literal Account to the Opportunity, which (surprisingly) doesn't actually assign the ID of the account to the opportunity. What you're really doing is assigning the pseudo-sobject Opportunity.Account the literal SObject of Account, which is an in-memory-only reference to the account without actually assigning the ID of the account to the opportunity. Confusing? Yea, I know, it's somewhat hard to explain. Just know that you must always assign an ID value if you wish to have records be related to each other (or use Upsert with the optional External ID, but that's a topic for another day...).

All Answers

sfdcfoxsfdcfox

Assign acc01.Id to opp01.AccountId instead. You're assigning the literal Account to the Opportunity, which (surprisingly) doesn't actually assign the ID of the account to the opportunity. What you're really doing is assigning the pseudo-sobject Opportunity.Account the literal SObject of Account, which is an in-memory-only reference to the account without actually assigning the ID of the account to the opportunity. Confusing? Yea, I know, it's somewhat hard to explain. Just know that you must always assign an ID value if you wish to have records be related to each other (or use Upsert with the optional External ID, but that's a topic for another day...).

This was selected as the best answer
FlorentFlorent

Hello sfdcfox,

 

Thanks a lot for your reply: it works!

 

I indeed assumed that opp01.Account = acc01 would assign a pointer to the account object, allowing me to access all account values…

I' m not sure I understand what you mean with "assigning the literal SObject of Account […] without actually assigning the ID of the account to the opportunity", but I'm sure I'll do some research on that.

 

Anyway, using opp01.AccountId = acc01.Id does the trick (which I do find weirder to use than the literal assignment, but there you go).

 

So thanks a lot for taking time to read and answer my request.

 

Cheers,

Florent