System.TypeException: DML operation INSERT not allowed on __Share - salesforce

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?

Related

Salesforce Lookup field showing ID value instead of record name

Hi I have a custom object which has a custom field of type lookup to Account. The child custom record gets created when Account record is created. I am creating child record in after insert trigger on Account and keeping the name of child record same as of the name of Account.
But In the name I am getting 15 digit Id instead of real name of account. What can be the issue?
Code## for Trigger
if(Trigger.isAfter && (Trigger.isInsert)){
AccountTriggerHelper.createReferrer(Trigger.new);
}
In Apex Class:
public static void createReferrer(List<Account> accountList){
List<Referral__c> newReferral = new List<Referral__c>();
for(Account acc : accountList) {
if(acc.Talos_RecordType__c=='Referrer'){
system.debug('==acc=1='+acc);
Referral__c ref = new Referral__c();
System.debug('-----acc.Name----'+acc.Name);
ref.Name = acc.Name;
ref.Account__c = acc.Id;
ref.Email__c = acc.PersonEmail;
ref.Talos_IQOS_Expert_ID__c = acc.Talos_IQOS_Expert_ID__c;
ref.Talos_Referrer_Type__c = acc.Referrer_Type__c;
ref.User_Id__c = acc.UserID__c;
ref.Email_Validation__c = true;
ref.Store_Code__c = acc.Store_Code__c;
newReferral.add(ref);
system.debug('==referral=1='+newReferral);
}
}
Thanks,
Marc
This code can't lead to population of Id in the Name field. There might be another trigger on Account or child Object, or any workflow, process builder on your org creating that change again. Can you check the created date and modified date of the child record.
If they are different, then most probably I am right.

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).

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.

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

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.

Problem with altering model attributes in controller

Today I've got a problem when I tried using following code to alter the model attribute in the controller
function userlist($trigger = 1)
{
if($trigger == 1)
{
$this->User->useTable = 'betausers'; //'betausers' is completely the same structure as table 'users'
}
$users = $this->User->find('all');
debug($users);
}
And the model file is
class User extends AppModel
{
var $name = "User";
//var $useTable = 'betausers';
function beforeFind() //only for debug
{
debug($this->useTable);
}
}
The debug message in the model showed the userTable attribute had been changed to betausers.And It was supposed to show all records in table betausers.However,I still got the data in the users,which quite confused me.And I hope someone can show me some directions to solve this problem.
Regards
Model::useTable is only consulted during model instantiation (see the API documentation for Model::__construct). If you want to change the model's table on the fly, you should use Model::setSource:
if ( $trigger == 1 ) {
$this->User->setSource('betausers');
}
The table to use is "fixed" when the model is loaded/instantiated. At that time a DB connection object is created, the table schema is being checked and a lot of other things happen. You can change that variable later all you want, Cake is not looking at it anymore after that point.
One model should be associated with one table, and that association shouldn't change during runtime. You'll need to make another model BetaUser and dynamically change the model you're using. Or rethink your database schema, a simple flag to distinguish beta users from regular users within the users table may be better than a whole new table.

Resources