How can I get the name of the Lead Owner in a Lead custom formula field? - salesforce

I've got an application that reads Lead records from Salesforce via the API and I want to link the Lead Owner field to an attribute in the application. The Lead Owner field doesn't up in the list of available fields but all the custom fields do.
So, my first attempt at a solution was to create a custom field that displayed the Lead Owner name. In the SF formula editor, as far as I can tell, it doesn't display the actual data field but instead displays the ID string. Which is pretty meaningless in the context that I need it for.
alt text http://skinny.fire-storm.net/forposting/insertfield.JPG
Is there a way that we can get at the data in the object that the ID string references?
alt text http://skinny.fire-storm.net/forposting/havewant.JPG
I have the RED BOX but need the GREEN BOX.
EDIT: I can't change the application code that calls the API. I can only change salesforce. So, this is more of a salesforce superuser / formula-writer question, not a question about writing code that calls the SF API.

Salesforce allows access to related data through what they call relationship queries. Instead of joining, you specify the query like this:
System.debug([SELECT Owner.Name FROM Lead WHERE Id = '00QS00000037lvv'].Owner.Name);
Try running that in the system log, just replace the lead ID with one that you're looking at.
When accessing the data through the API, the principle is the same, your proxy objects should allow you to access Lead.Owner.Name.
EDIT:
I agree with eyescream, since you can't change the application code, creating an Apex trigger would be the best way to go here. Here's some code to get you started:
trigger Lead_UpdateOwner on Lead(before insert, before update)
{
Map<Id, String> ownerMap = new Map<Id, String>();
for (Lead lead : Trigger.new)
{
ownerMap.put(lead.OwnerId, null);
}
if (ownerMap.size() > 0)
{
for (User[] users : [SELECT Id, Name FROM User WHERE Id IN :ownerMap.keySet()])
{
for (Integer i=0; i<users.size(); i++)
{
ownerMap.put(users[i].Id, users[i].Name);
}
}
for (Lead lead : Trigger.new)
{
lead.OwnerName__c = ownerMap.get(lead.OwnerId);
}
}
}
lead.OwnerName__c would need to be the name of your custom field on the lead object that will hold the owner name. Type Text, length 121.

I had a similar problem, but wanted all the current and future User fields available. Since a custom lookup field to the User is not restricted by formula fields, I created one named
OwnerLookup
on the Opportunity and Account objects, then used a triggers to populate it on creation or edit. For example the Opportunity trigger is this:
trigger OpportunityTrigger on Opportunity (before insert, after insert, before update, after update) {
if(trigger.isBefore && trigger.isInsert) {
OpportunityTriggerHandler.newOpportunity(Trigger.old, Trigger.new);
}
else if(trigger.isAfter && trigger.isInsert){
//OpportunityTriggerHandler.futureUse(Trigger.new);
}
else if(trigger.isBefore && trigger.isUpdate){
OpportunityTriggerHandler.updateOpportunity(Trigger.new, Trigger.oldMap);
}
else if(trigger.isAfter && trigger.isUpdate){
//OpportunityTriggerHandler.futureUse(Trigger.new, Trigger.oldMap);
}
}
and the OpportunityTriggerHandler class (Apex code) is:
public with sharing class OpportunityTriggerHandler {
public static void newOpportunity( List<Opportunity> oldOpportunitys, List<Opportunity> newOpportunitys ) {
for (Opportunity opp: newOpportunitys) {
updateOwnerData( opp );
}
}
public static void updateOpportunity( List<Opportunity> oldOpportunitys, Map<Id, Opportunity> newOpportunitys ) {
for (Opportunity opp: oldOpportunitys) {
updateOwnerData( opp );
}
}
public static void updateOwnerData( Opportunity opp ) {
opp.OwnerLookup__c = opp.OwnerId;
}
}
I then create Formula fields on the Opportunity/Account objects to get to any of the owner (User) object fields, such as Oppty Owner Name formula field:
OwnerLookup__r.FirstName & " " & OwnerLookup__r.LastName

VLOOKUP function would be a good try, but
it's available only in validation rules, not in field definitions
it can be used only on custom objects and you need data from User
I'd say you need to query from your application for
SELECT Owner.FirstName, Owner.LastName FROM Lead
Other than that... some "after insert, after update" trigger that would populate your custom field when owner changes?

Just posting for completeness sake (and for the Google searches): The issue arises with any object that can be queued, not just Lead, since the source of it is that the owner can refer to either a user (as usual) or a queue object.
This can be resolved using a formula field instead of triggers, like below:
BLANKVALUE(Owner:Queue.QueueName, Owner:User.FirstName & " " & Owner:User.LastName)
Basically, the BLANKVALUE function in the formula checks whether the owner.queuename is blank, and if so gives the name of the user.

Related

How do I get info from lookup in salesforce query

I have a custom salesforce object Installation__c and it has a custom field Product__c which is a lookup to a custom object Product__c I am trying to get the fields from the child object using these query:
public with sharing class InstallationController {
#AuraEnabled
public static List<Installation__c> getItems() {
// Perform isAccessible() checking first, then
return [SELECT Id, Name, Installation_Display_Name__c, Product__c, Status__c, (SELECT Product__c.Name FROM Product__c) FROM Installation__c];
}
}
I get the error:
Didn't understand relationship 'Product__c' in FROM part of query call. If you are attempting to use a custom relationship, be sure to append the '__r' after the custom relationship name. Please reference your WSDL or the describe call for the appropriate names.
I have tried changing the Query to
FROM Product__rand FROM Product__c__r but neither seems to work, how do I fix my query?
If you're traversing up or down a relationship hierarchy, the __c suffix becomes __r (r for 'relationship') until you finally get to the field that you're looking for (which still ends in __c if it's a custom field). So in your case, it will be
public with sharing class InstallationController {
#AuraEnabled
public static List<Installation__c> getItems() {
// Perform isAccessible() checking first, then
return [SELECT Id, Name, Installation_Display_Name__c, Product__r.Name, Status__c FROM Installation__c];
}
}
So, the change here is, for the relationship you have to use Product__r.Name
Click into the relationship on the object that has the look up. Copy the relationship name adding __r to it
This example would be Test_Drives__r

System.TypeException: DML operation INSERT not allowed on __Share

I have created share object in my salesforce environment.
From Setup-> security Controls -> Sharing Settings -> I made my custom object access to "Private". So suppose my object name is EVT_Client__c & I am trying to insert data into EVT_Client__Share from trigger(AfterInsert) I am unable to do it. I am getting error as given below.
Once data get inserted into EVT_Client__c object our trigger tries to insert data into EVT_Client__share object but we are getting error. Profile user who is inserting data into EVT_Client__c object through UI having Read/create/edit/delete access on object.
System.TypeException: DML operation INSERT not allowed on
EVT_Client__Share
My code in trigger is as below in which I am getting error.
public with sharing class EVT_Client_TriggerHandler {
private void ShareClientToThirdPartyJobGroup(List<EVT_Client__c> lstClients){
List<EVT_Client__Share> lstSharesForThirdPartyGroup = new List<EVT_Client__Share>();
List<Group> lstThirdPartyGroups = [Select Id, RelatedId from Group where Name = 'Third Party'];
for(EVT_Client__c client: lstClients){
for(Group roleGroup : lstThirdPartyGroups ){
EVT_Client___Share objShareForThirdPartyGroup = new EVT_Client__Share(ParentId = client.Id,
UserOrGroupId = roleGroup.Id,
AccessLevel = 'Edit',
RowCause = Schema.EVT_Client__Share.RowCause.User_Client__Access__c);
lstSharesForThirdPartyGroup.add(objShareForThirdPartyGroup);
}
}
insert lstSharesForThirdPartyGroup;
}
}
You're sure it's not a "detail" in any master-detail relationship? The OWD would then say "controlled by parent" instead of "private". Can you switch to Classic view, go to page layout and verify the [Sharing] button can be added, displays OK, this user can manually share this record from UI?
Post your code sample? I suspect you need to define Apex Sharing Reason first. It's bit like roles if you ever played with Account/Contact/Opportunity/Case roles.
Once it's set you should be able to do something like this code sample: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_bulk_sharing_creating_with_apex.htm (they use "manual" as sharing reason but you might have more luck with custom sharing reason.
I quickly created your object and this worked like a charm for me:
trigger Stack57201752 on EVT_Client__c (after insert) {
List<Group> groups = [SELECT Id FROM Group WHERE Name = 'Third Party'];
List<EVT_Client__Share> shares = new List<EVT_Client__Share>();
for(EVT_Client__c c : trigger.new){
for(Group g : groups){
shares.add(new EVT_Client__Share(
ParentId = c.Id,
UserOrGroupId = g.Id,
AccessLevel = 'Edit',
RowCause = 'Manual'
));
}
}
insert shares;
}
And when I click the [Sharing] button on the record it looks good:
So... Stupid question time: have you clicked this checkbox?

Entity Framework Attach not updating DB

I have the following problem: when i try to change the town of a user by passing the modified user entry to my ModifyUser method in my UserService class everything seems ok but the changes are not applied in the database.
I tried the solutions from similar problems i found on stackoverflow but none of them seem to work.
As they say "a picture is worth a thousand words" take a look at this, since its easier to understand compared to my short explanation.
http://prntscr.com/el51z0
else if (property == "BornTown")
{
if (this.townService.TownExists(value))
{
User user = this.userService.GetUserByName(username);
Town town = this.townService.GetTownByName(value);
user.BornTown = town;
this.userService.ModifyUser(user);
}
else
{
Console.WriteLine($"Value {value} is not valid.");
throw new ArgumentException($"Town {value} not found!");
}
}
When i pass the user to the ModifyUser the database does not update, and BornTown stays NULL.
public void ModifyUser(User user)
{
using (PhotoShareContext context = new PhotoShareContext())
{
context.Users.Attach(user);
context.Entry(user).State = EntityState.Modified;
context.SaveChanges();
}
}
Or else try below code:
User user = context.users.where(x=>x.username == username).FirstOrDefault();
user.BornTown = town; //string
context.savechanges();
Can you try
context.Users.where(x=>x.username == username).FirstOrDefault();
and don't use any()
Can you check if you are getting the proper username, by again checking the username once you are getting back the value in the function.
Instead of manipulating the navigation properties directly try to manage the entity relation using it's foreign keys:
Town town = this.townService.GetTownByName(value);
user.BornTownId = town.Id;
Keep in mind that if you have not created explicit foreign keys in your model entity you should map the existing one: take a look at "Users" table - see how the foreign key column is named- it should look something like UserTown_Id. Then put it in your model class (you may add foreign key attribute or map it in model builder).
Be careful when adding the new property to your model (it can make your optional relation with "Towns" required).

Appengine entity update not working

I am developing code for app engine. I tried to update an existing row by updating the same entity returned as result of query. But it creates new row instead of updating the same row. Following is the code:
public boolean updateProfile(DbProfile profile) {
Transaction txn = _datastore.beginTransaction();
Entity entity = getProfileEntity(profile.getLoginId());
if (entity != null) {
entity.setProperty(DbProfile.DbProfilePropertyNames.address, profile.getAddress());
entity.setProperty(DbProfile.DbProfilePropertyNames.name, profile.getName());
Key key = _datastore.put(entity);
txn.commit();
return true;
}
return false;
}
private Entity getProfileEntity(String userName) {
Key eRecommendationKey = KeyFactory.createKey("eRecommendation", _dbKeyName);
FilterPredicate predicateUsername =
new FilterPredicate(DbProfile.DbProfilePropertyNames.loginId, FilterOperator.EQUAL,
userName.toUpperCase());
Query query =
new Query(DbProfile.entityProfileName, eRecommendationKey).setFilter(predicateUsername);
List<Entity> profiles =
_datastore.prepare(query).asList(FetchOptions.Builder.withDefaults());
Utils.log.log( Level.SEVERE, "not found"+profiles.size() );
if (profiles.size() == 0) {
//profile data is not set yet
return null;
} else {
return profiles.get(0);
}
}
Following image shows fields in the entity.
Please let me know how can I fix the issue.
My Java skills are not too good, so I find it difficult to understand your code sample. I also don't see where updateProfile() is called and how your code is getting or constructing the profile object, especially whether the key of the profile object is altered.
But in general, if new entities are created instead of updating existing entities, the reason is that the key at your updating commit is different from the actual key of the existing entity.
Typical situations:
a string (key-name) is used instead of an integer (ID), or vice versa
a typo in the kind name of the key
different namespace or app properties of the key
parents are missing in the key path or are constructed wrongly
Suggestion:
In datastore viewer, compare the key of an existing entity with the key of the accidentally created entity. The difference between both keys might give you a hint where to look in your code for the bug.
I solved the problem. It was my mistake. I had called saveData servlet instead of updateProfile servlet.

Apex Trigger Context Variable

Here my code for apex trigger.
trigger LeadTrigger on Lead (after insert)
{
if(Trigger.isInsert){
for(Lead newLead: Trigger.new)
{
//newLead.RecrodTypeId //'Give value of record type id.
//newLead.RecordType.Name //'Null'
}
}
}
Why "newLead.RecordType.Name" returns null?
The lists of objects available in triggers only have values for the fields on the object the trigger is running on. No relationships are traversed, only the IDs of the lookup records are included.
Therefore, to pull in any extra information you need to from related objects needs to be queried for.
You'll want to do something like this:
trigger LeadTrigger on Lead (after insert) {
map<id, RecordType> mapRecordTypes = new map<id, RecordType>();
if(Trigger.isInsert) {
for(Lead newLead: Trigger.new) {
mapRecordTypes.put(newLead.RecordTypeId, null);
}
}
for(RecordType rt : [select Id, Name from RecordType
where Id in : mapRecordTypes.ketSet()]) {
mapRecordTypes.put(rt.Id, rt);
}
for(Lead newLead : Trigger.new) {
string recordTypeName = mapRecordTypes.get(sLead.RecordTypeId).Name;
}
}
This is probably because some of your leads that just got inserted don't have record types associated with them. This is normal. You can enforce that record type selection is mandatory through configuration, if that's what you're looking for.
[EDIT]
Now I think I understand the issue (from your comment). The reason is that since you're in a trigger, the associated RecordType referenced object is not available. The RecordTypeId will always be available since it is literally part of the trigger object as an Id. However, child objects (referenced objects) will not be available to simply reference from within a trigger. To do this you need to create a map of the referenced object in question by doing an additional SOQL call WHERE Id IN: theIdList.
From Apex, not in a trigger, you need to specifically call this field out from your SOQL like this:
List<Lead> leads = [SELECT Id, RecordType.Name FROM Lead];
What just happened there is that the child object, the RecordType in this case, was included in the query and therefore available to you. By default a trigger will not have all of your child objects pre-selected and therefore need to be selected afterwards from within the trigger or class called by the trigger:
List<Id> recIds = new List<Id>();
for(Lead l : leads)
{
recIds.add(l.RecordTypeId);
}
List<RecordType> rt = [SELECT Id, Name FROM RecordType WHERE Id IN :recIds];
Map <Id, String> idRecNameMap = new Map<Id, String>();
for(RecordType r : rt)
{
idRecNameMap.put(r.Id, r.Name);
}
// And finally...
for(Lead l : Trigger.new)
{
String tmpRecordTypeName = idRecNameMap.get(l.RecordTypeId);
}
I did not test this code but I think it look ok. Hope this makes sense.
you can't get extra information on the related objects from this trigger. if you want to get more information you need to make query for other objects.
List<RecordType> records = [SELECT Id, Name FROM RecordType WHERE Id = newLead.RecrodTypeId];
string myname = records[0].name;
but remember that you shouldn't make a query in for loop. so if you wanted to do it in the right way go for Adam's solution.
Put some system debug inside the loop and check your system debug logs for more information
system.debug('lead:' + newLead);
inside the for loop and see what is being passed in. You may find that it is null.
We cant really give you a good answer without knowint the rest of your set up.

Resources