Formula fields are one of two types of "calculated value" field allowed in SalesForce. (The other type being lookup fields, which do not allow any calculations besides aggregations.)
As I mentioned, while most universities choose either custom "Admission" objects or the SalesForce native "Opportunity" object to represent the many-to-one nature of people trying to matriculate to their school, we are forced to live in, and synchronize, both models.
Keeping this data synchronized requires code to perform a cardinality reduction. I used a formula field to do the heavy lifting.
Note: If you want to skip the background and go straight to the formula field's source code, click here.
Imagine that you're building a garden rake with tines at both ends of the handle. (The handle is a particular "Contact." Each tine at 1 end of the rake represents "Admissions" records. Each tine at the other end of the rake represents "Opportunity" records.)
- You allow the neighbor's children to add tines to the "Admissions" end of the rake.
(This is like the Central Admissions Office adding records about paperwork coming into their office from the Contact.)- The neighbor's children are allowed to draw pretty pictures on the "Admissions" tines before they attach them to the rake.
(This is like the Central Admissions office recording the details of that paperwork.)
- The neighbor's children are allowed to draw pretty pictures on the "Admissions" tines before they attach them to the rake.
- You allow your own children add tines to the "Opportunities" end of the rake.
(This is like your department adding records about their intuition that someone is interested in matriculating based on phone calls, event attendance, etc.)- Your are allowed to draw pretty pictures on the "Opportunities" tines before they attach them to the rake.
(This is like your office recording the details of when they think the person would want to matriculate and other relevant information.)
- Your are allowed to draw pretty pictures on the "Opportunities" tines before they attach them to the rake.
You have a special rule for your children (working on the "Opportunities" tines): Although they're not allowed to add tines to the "Admissions" end of the rake, periodically they must look at the "Admissions" tines that have already been attached. If they see that there is an "Admissions" tine attached to the rake that is the same color as one of the "Opportunity" tines that they have attached, they must copy over all of the pretty pictures from that "Admissions" tine onto their matching "Opportunity" tine.
(This matches our business rule that SalesForce must push all data from "Admissions" objects to their matching "Opportunity" objects so that our recruiters can see everything they need to see on "Opportunity" objects.)
That's the kind of environment we're developing in.
There's just one more catch:
Our Central Admissions Office is allowed to add multiple tines of a given color, whereas our Departmental Recruiters must make every tine at their end a unique color. (The color of a tine is analogous to "Program Of Interest.")
The ERP requires that every Person-Program-Term combination be given its own record. Our recruiters don't care about that level of detail - they just want to see the "most relevant" information for a given Person-Program combination.
So we have a cardinality reduction problem on our hands. This lets the "doodle-copying" code know which "blue tine" on the "Admissions" side of the rake is the "best" so that we can copy its doodles over to the "blue tine" on the "Opportunity" side of the rake. Otherwise, the "doodle-copying" code will get confused.
Here's how I solved it: (update here)
First, I created a formula field on Admissions called Record_Type_Prioritizer__c.
I should explain that: I've oversimplified our problem up above. We actually have 2 similar record types coming in from the ERP - "Recruit" and "Admissions." So Record_Type_Prioritizer__c has objects label themselves 9 (if Admissions), 8 (if Recruit w/ an "app started" flag), or 7 (plain-old recruit).
Next, I created a formula field on Admissions called Winner_Picker__c. (It's also on "Recruit.")
Here is how I described Winner_Picker__c to end users so they could review my logic:
WinnerPicker is a calculated numeric field. First, it disqualifies any "past" records or "withdrawn" by being blank for such records. Then it ranks Admissions records as best, Recruit records with an Application Started? flag as 2nd-best, and other Recruit records in 3rd place. Within each of those categories, it ranks records in descending order from soonest term (best) to futuremost term (worst). If there are still any ties, it breaks them "coin toss" style by ranking them in descending order according to their unique record IDs from Banner.
Here is the math behind this formula:
- Return null for any term that has already started or any Admissions record with an inactive status
- Admissions = 9,000,000; Recruit+Flag = 8,000,000, Recruit-flag = 7,000,000.
- Subtract term code (a 6-digit #; subtraction because we care about sooner terms more than later ones as long as they’re in the future).
- Multiply that 7-digit # by 1,000,000 to add a comfortable amount of zero-padding at the right.
- Add 900 or 800 or 700 (Adm / Recr+Flag / Recr) because the last 2 digits are unique within, but not across, sets of Admissions/Recruit records for a person.
- Add the 2 unique digits not already covered in the previous code from the Admissions/Recruit record's primary key in the ERP as a tie-breaker.
Finally, here is the source code of Admissions.Winner_Picker__c:
Note that Admissions's "Term" field is a Lookup Field to a "Term" object. "Term" records are full of fun formula fields themselves! They provide data about a given term in many formats: as a sortable number, as a comparison of its start date to today, etc.
IF(
Term_Lookup__r.Future__c ,
IF(
OR(
ISPICKVAL(Status__c, "Deferred"),
ISPICKVAL(Status__c, "Rejected"),
ISPICKVAL(Status__c, "Withdrawn")
)
,
null,
(((Record_Type_Prioritizer__c * 1000000) - Term_Lookup__r.Term_Code_Numeric__c)* 1000000)
+
((Record_Type_Prioritizer__c * 100) + VALUE(RIGHT( Banner_Application_ID__c , ( (LEN(Banner_Application_ID__c) - 6) - LEN(Formula_Contact_PIDM__c) ) )))
)
, null
)
The Result:
Each "Admission" record looks at its field values and suggests its "self-worth" as a number.
This "self-worth" number is guaranteed to be a unique value among all Admissions+Recruit records with the same Person+Program combination.
Thanks to this formula field, you simply have to perform an "IS NOT NULL" and a MAX() to retrieve the best ERP-imported record for a Program.
(Or, in other words, "The best blue tine on the rake.")
It saved us a lot of time during a Requirements Analysis meeting with our Partners doing trigger development work to say, "Don't worry about the cardinality reduction logic - we got it - just MAX() this field for the Person+Program."
Now if only you could use a Rollup Field to MAX() Formula Fields ... Don't forget to click here and upvote! :-)
Hey K- you already have fans here at Redpath CG, and the question has been asked: Does she have a Twitter handle??
ReplyDeleteAwww, you're so sweet, you guys are making me blush!
ReplyDeleteAt the moment, I don't have Twitter. I'm quite a luddite + long-winded.
Any idea why they ask? (That is, what people do with Twitter?)
- Should I expect that people would want to get ahold of me through it and be ready to keep an eye on it if I get such an account?
- Are the "I have a new post!" announcements an alternative to RSS feeds for some people?
- Do I need to have more to say than "I have a new post" or risk letting people down?
:-)