Trigger to restrict duplicate record for a particular type - salesforce

I have a custom object consent and preferences which is child to account.
Requirement is to restrict duplicate record based on channel field.
foe example if i have created a consent of channel email it should throw error when i try to create second record with same email as channel.
The below is the code i have written,but it is letting me create only one record .for the second record irrespective of the channel its throwing me the error:
Trigger code:
set<string> newChannelSet = new set<string>();
set<string> dbChannelSet = new set<string>();
for(PE_ConsentPreferences__c newCon : trigger.new){
newChannelSet.add(newCon.PE_Channel__c);
}
for(PE_ConsentPreferences__c dbcon : [select id, PE_Channel__c from PE_ConsentPreferences__c where PE_Channel__c IN: newChannelSet]){
dbChannelSet.add(dbcon.PE_Channel__c);
}
for(PE_ConsentPreferences__c newConsent : trigger.new){
if(dbChannelSet.contains(newConsent.PE_Channel__c))
newConsent.addError('You are inserting Duplicate record');
}

Your trigger blocks you because you didn't filter by Account in the query. So it'll let you add 1 record of each channel type and that's all.
I recommend not doing it with code. It is going to get crazier than you think really fast.
You need to stop inserts. To do that you need to compare against values already in the database (fine) but also you should protect against mass loading with Data Loader for example. So you need to compare against other records in trigger.new. You can kind of simplify it if you move logic from before insert to after insert, you can then query everything from DB... But it's weak, it's a validation that should prevent save, it logically belongs in before. It'll waste account id, maybe some autonumbers... Not elegant.
On update you should handle update of Channel but also of Account Id (reparenting to another record!). Otherwise I'll create consent with acc1 and move it to acc2.
What about undelete scenario? I create 1 consent, delete it, create identical one and restore 1st one from Recycle Bin. If you didn't cover after undelete - boom, headshot.
Instead go with pure config route (or simple trigger), let the database handle that for you.
Make a helper text field, mark it unique.
Write a workflow / process builder / simple trigger (before insert, before update) that writes to this field combination of Account__c + ' ' + PE_Channel__c. Condition could be ISNEW() || ISCHANGED(Account__c) || ISCHANGED(PE_Channel__c)
Optionally prepare data fix to update existing records.
Job done, you can't break it now. And if you ever need to allow more combinations (3rd field) it's easy for admin to extend it. As long as you keep under 255 chars total.
Or (even better) there are duplicate matching rules ;) give them a go before you do anything custom? Maybe check https://trailhead.salesforce.com/en/content/learn/modules/sales_admin_duplicate_management out.

Related

I want to change opportunity.createdby to lead.ownerid,rectify my code

I want to change opportunity.CreatedById to lead.ownerid,rectify my code
trigger on opportunity(after insert)
lst<ooportunity> opps=[select opportunityId,CreatedById from opportunity where opportunityId IN
: Trigger.New];
list<lead> leads=[SELECT Id,ConvertedOpportunityId,OwnerId From lead where isConverted=true];
for(opportunity ops:opps)
{if(leads.Id==opps.OpportunityId)
{ opps.CreatedById=leads.OwnerId;
ops.add(opps);
}
update ops;
You can't, at least not like that.
Audit fields (createdbyid, createddate, lastmodifiedbyid, lastmodifieddate) are readonly and modified by SF internally.
There's a trick to open them up for example for data migrations (you're loading 7 years worth of opportunities, you want to record their real creation date so it doesn't show as a spike in reports). But you have only 1 shot at this. Only during insert.
https://help.salesforce.com/s/articleView?id=000334139&type=1 and https://help.salesforce.com/s/articleView?id=000331038&type=1
You'd need to tick something in setup, this causes 2 magic checkboxes to show up in profiles/permission sets. Then you'd edit your profile (or create new permission set and assign to yourself).
You'd need to change your trigger to before insert (but if it's before - the lead is still being converted, the opp id isn't known yet - so you can't match it by Lead.OpportunityId)... And even then - this feature is aimed at sysadmins, I doubt normal user without "Modify All Data" can do it.
Rethink your business scenario. Maybe you want to set Opportunity.OwnerId, not CreatedById?

Salesforce Apex not updating record

I am trying to do a simple update in sandbox on a field that is in the Account Object in Salesforce but it does not seem like it is committing. I follow the basics of pulling and updating a record as stated below:
String accountId = '12345'
Account queriedAccount = [SELECT Id, Communication_Bubble__c FROM Account WHERE id = :accountId limit 1];
System.debug(queriedAccount);
// set the field i want to update
queriedAccount.Communication_Bubble__c = 'New Donor';
// update field
update queriedAccount;
// run select again to see if update went through
Account queriedAccount2 = [SELECT Id, Communication_Bubble__c FROM Account WHERE id = :accountId limit 1];
System.debug(queriedAccount2);
Looking at the logs after this piece of code is ran shows that the update went through as I was able to pull the account and it displays the Account with the updated field. Here are what both of the debugs display:
Debug1: |Debug|Account:{Id=12345} (communication bubble is null)
Debug2: |Debug|Account:{Id=12345,Communication_Bubble=New Donor}
From the above it seems like the update is working exactly how it should be. But when I go to the account in the dashboard, or pull the account with a query in the developer console that Communication Bubble field is still blank.
It seems like I am missing some sort of commit but I cant figure out what it is.
No way this compiles, did you anonymise it too much?
Communication_Bubble - actual field name is probably Communication_Bubble__c.
You query and set a field value on queriedAccount but what you actually save is another variable queriedContact?
There's low chance you actually have some code that resets the value (would have to be #future, time-based workflow etc to work but not be detected in 2nd query... But my vote goes to "you just updated wrong record"

Salesforce Junction Objects

To all salesforce experts i need some assistance. I have my contacts and a custom object named programs. I created a junction object using to master detail relationships with contacts and programs. I want to avoid relating the same contact to the same program. I tried triggers but I couldn't create the testing part to use it outside sandbox.
I went back to the basics and created a Unique text field. I tried to use default value but EVERYTHING i write in that crap is wrong -_-. I tried Contact__r.Email & "-" & Program__r.Name but to no avail.
I tried workflow rules with a field update but my field update NEVER runs.(Yes I did activate the workflow rule) and I didn't know what to write in my rule's code.
The workflow firing condition could be simply a formula that says true. Alternatively use "every time record is inserted". It also depends whether your master-details are set once and that's it or they will be "reparentable" (option introduced in Summer '12 I think). Maybe post a screenshot / text description of your firing condition? Also - is your unique field set to "case sensitive"?
As for the formula to populate the unique field - something like Contact__c + ' ' + Program__c (or whatever the API names of your fields are) should be OK. Don't use Contact__r.Email etc as these don't have to be unique...
You'll have to somehow fill in the uniqueness criteria for all existing records (maybe that's why you claimed it doesn't work?). If you can use Apex for data fixes - something like this should get you started.
List<Junction__c> junctions = [SELECT Contact__c, Program__c
FROM Junction__c
WHERE Unique_Text_Field__c = null
LIMIT 10000];
for(Junction__c j : junctions){
String key = String.valueOf(j.Contact__c).left(15) + ' ' + String.valueOf(j.Program__c).left(15);
j.Unique_Text_Field__c = key;
}
update junctions;
Keep rerunning it until it starts to show 0 rows processed. The Ids are cut down to 15 chars because in Apex you'd usually see full 18-char Id but workflows use 15-char versions.

Trigger Duplicate CSV

am trying to upload a CSV file / insert a bulk of records using the import wizard. In short I would like to keep the latest record, in case if duplicates are found. Duplicates record are a combination of First name, Last name and title
For example if my CSV file looks like the following:
James,Wistler,34,New York,Married
James,Wistler,34,London,Married
....
....
James,Wistler,34,New York,Divorced
This should only keep in my org: James,Wistler,34,New York,Divorced
I have been trying to write a trigger before an update / insert but so far no success Here is my trigger code: (The code is not yet finished (only filering with Firstname), I am having a problem deleting found duplicate in my CSV ) Any hints. Thanks for reading!
trigger CheckDuplicateInsert on Customer__c(before insert,before update){
Map <String, Customer__c> customerFirstName = new Map<String,Customer__c>();
list <Customer__c> CustomerList = Trigger.new;
for (Customer__c newCustomer : CustomerList)
{
if ((newCustomer.First_Name__c != null) && System.Trigger.isInsert )
{
if (customerFirstName.containsKey(newCustomer.First_Name__c) )
//remove the duplicate from the map
customerFirstName.remove(newCustomer.First_Name__c);
//end of the if clause
// add this stage we dont have any duplicate, so lets add a new customer
customerFirstName.put(newCustomer.First_Name__c , newCustomer);
}
else if ((System.Trigger.oldMap.get(newCustomer.id)!= null)&&newCustomer.First_Name__c !=System.Trigger.oldMap.get(newCustomer.id).First_Name__c )
{//field is being updated, lets mark it with UPDATED for tracking
newCustomer.First_Name__c=newCustomer.First_Name__c+'UPDATED';
customerFirstName.put(newCustomer.First_Name__c , newCustomer);
}
}
for (Customer__c customer : [SELECT First_Name__c FROM Customer__c WHERE First_Name__c IN :customerFirstName.KeySet()])
{
if (customer.First_Name__c!=null)
{
Customer__c newCustomer=customerFirstName.get(customer.First_Name__c);
newCustomer.First_Name__c=Customer.First_Name__c+'EXIST_DB';
}
}
}
Purely non-SF solution would be to sort them & deduplicate in Excel for example ;)
Good news - you don't need a trigger. Bad news - you might have to ditch the import wizard and start using Data Loader. The solution is pretty long and looks scary but once you get the hang of it it should start to make more sense and be easier to maintain in future than writing code.
You can download the Data Loader in setup area of your Production org and here's some basic info about the tool.
Anyway.
I'd make a new text field on your Contact, call it "unique key" or something and mark it as External Id. If you have never used ext. ids - Jeff Douglas has a good post about them.
You might have to populate the field on your existing data before proceeding. Easiest would be to export all Contacts where it's blank (from a report for example), fill it in with some Excel formulas and import back.
If you want, you can even write a workflow rule to handle the generation of the unique key. This might help you when Mrs. Jane Doe gets married and becomes Jane Bloggs and also will make previous point easier (you'd just import Contacts without changes, just "touching" them and the workflow will fire). Something like
condition: ISBLANK(Unique_key__c) || ISCHANGED(FirstName) || ISCHANGED(LastName) || ISCHANGED(Title)
new value: Title + FirstName + ' ' + LastName
Almost there. Fire Data Loader and prepare an upsert job (because we want to insert some records and when duplicate is found - update them instead).
My only concern is what happens when what's effectively same row will appear more than once in 1 "batch" of records sent to SF like in your example. Upsert will not know which value is valid (it's like setting x = 7; and x = 5; in same save to DB) and will decide to fail these rows. So you might have to tweak the amount of records in a batch in Data Loader's settings.

Account name must be unique

What I am looking to do is Make it the "Account" name field require a unique name.
Basically If one of my reps tries to create an account, and that account all ready exists it tells them no that account all ready exists.
Salesforce tells me this funicality is not build into sales force. Any help or dirrection would we wonderfull.
Make a new text field, call it Name__c. Mark it as unique, length... probably 80, same as Name field length.
Create new Workflow rule with condition ISNEW() || ISCHANGED(Name) || ISBLANK(Name__c) and the action should be a field update that simply has Name in the formula that determines new value.
Remember to activate the workflow and to fill in the newly created field because it will be blank for all your existing accounts!
Your call if you want to show the field on page layouts (it's quite "technical" so could be hidden). If you do - it's a good idea to make it readonly!
You can use this validation:
AND(CONTAINS(VLOOKUP( $ObjectType.Account.Fields.Name , $ObjectType.Account.Fields.Name, Name), Name), OR(ISNEW(), ISCHANGED(Name)) )
Salesforce offers duplication management for this purpose.
You just set up Matching Rules and Duplicate Rules for your Account object in Setup > Administration Setup > Data.com Administration > Duplicate Management.
Link: https://help.salesforce.com/apex/HTViewHelpDoc?id=duplicate_prevention_map_of_tasks.htm&language=en_US
You could write a trigger to prevent duplicates. It'd be a "before insert" trigger that queried for existing accounts with the same name. If an Account name already exists, you'd call addError() on the new Account record, preventing the insert from continuing.
Have you searched the AppExchange for solutions? Might want to check out something like DupeCatcher
You could always make a custom field to contain the account name (something like "Business Name") and then ensure that's required and unique.
You'd need to do some basic Data Loader gymnastics to move your account names to the new field, and come up with a strategy for populating the existing Name field for accounts.
AND(VLOOKUP($ObjectType.Object_Name.Fields.Name, $ObjectType.Object_Name.Fields.Name, Name) = Name, OR(ISNEW(), ISCHANGED(Name)))

Resources