Prevent last contact from delete using trigger in salesforce - triggers

I am a newbie to salesforce and i have a requirement to which i need suggestions how to approach this requirement
I have 4 contacts related to one account and when someone deletes contacts, he should not be able to delete the last contact related to the account.For example: in account A1 i have 4 contacts and someone deletes the 3 contacts from that account then it should be deleted, after that there will be only 1 contact related to that account and den someone tries to delete the last contact than it should not be deleted.
How can i achieve this using trigger?

In your trigger, run a query for all contacts related to the account. If you are trying to delete all of them in this trigger, don't allow it. I don't know how you want to deal with people deleting multiple contacts at the same time, but let's say you will simply disallow the entire delete and the user has to retry with fewer contacts. If you want to come up with some logic that deletes all but 1 of the contacts, that's up to you.
Something like:
Trigger OnContactDelete on Contact (before delete) {
Set<ID> accountIds = new Set<ID>(); //all accounts that contacts are being deleted from
for (Contact contact : Trigger.old) {
accountIds.add(contact.AccountId);
}
List<Contact> contacts = [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountIds]; //get all of the contacts for all of the affected accounts
Map<ID, Set<ID>> deleteMap = new Map<ID, Set<ID>>(); //map an account ID to a set of contact IDs being deleted
Map<ID, Set<ID>> foundMap = new Map<ID, Set<ID>>(); //map an account ID to a set of contact IDs that were found by the query
for (Contact deleteContact : Trigger.old) {
Set<ID> idSet = deleteMap.get(deleteContact.AccountId);
if (idSet == null) {
idSet = new Set<ID>();
}
idSet.add(deleteContact.Id);
deleteMap.put(deleteContact.AccountId, idSet);
}
for (Contact foundContact : contacts) {
Set<ID> idSet = foundMap.get(foundContact.AccountId);
if (idSet == null) {
idSet = new Set<ID>();
}
idSet.add(foundContact.Id);
foundMap.put(foundContact.AccountId, idSet);
}
for (ID accountId : accountIds) { //go through each affected account
Set<ID> deleteIds = deleteMap.get(accountId);
Set<ID> foundIds = foundMap.get(accountId);
if (deleteIds != null && foundIds != null && deleteIds.containsAll(foundIds)) {
for (Contact contact : Trigger.old) {
if (deleteIds.contains(contact.Id)) { //this is one of the contacts being deleted
contact.addError('This contact is potentially the last contact for its account and cannot be deleted');
}
}
}
}
}
Note, I just typed this up in SO and haven't actually tested the code at all, even for syntax errors like missing semicolons or braces. It should work in theory, but there may be better ways of doing it.

You can solve this without triggers. You can create a Roll-Up Summary field on Account that counts the Contact records (ContactCount__c) and evaluate this count in a validation Rule on Account like this:
ContactCount__c = 0 && PRIORVALUE(ContactCount__c) > 0

Related

Prevent the deletion of Account which have related contact records having primary_contact__c check box = yes . (Salesforce trigger)

how do I know how many related contact record having primary_contact__c == true?
trigger AccountTrigger_1 on Account (before delete)
{
list <account> acclist = [select id,(select id, primary_contact__c from contacts) from account where id in : trigger.old];
for (account acc1 : trigger.old){
for(account acc2 :acclist ){
if(acc1.contacts.size() > 0){
acc1.adderror('you can not delete account');
}
}
}
}
You were almost there but that double for loop will explode if you ever do mass delete, 200x200 records to loop is bit evil.
This should work bit better.
trigger AccountTrigger_1 on Account (before delete)
{
Map<Id, Account> checks = new Map<Id, Account>([SELECT Id,
(SELECT Id
FROM Contacts
WHERE Primary_Contact__c = true
LIMIT 1)
FROM Account
WHERE Id IN :trigger.old
]);
for (account acc1 : trigger.old){
Account check = checks.get(acc1.Id);
if(!check.Contacts.isEmpty()){
acc1.adderror('you can not delete account');
}
}
}

APEX Trigger update picklist on child object

I am trying to do something simple (I hope).
I have 2x objects: Contact and Career_Path__c
Contact has one field: Currency__c (picklist)
Career_Path__c has one field: Next_Currency__c (picklist)
hese two objects are related via a loopup relationship from Career_Path__c up to Contact
I am trying to update the Next_Currency__c field every time a new Career_Path__c record is created. This does not seem to work. Thanks for any suggestions.
trigger Trigger_dynPicklist_2 on Contact (after insert) {
Map<Id, String> contactIdToCareerPathMap = new Map<Id, String>();
for (Contact c : Trigger.new) {
if (String.isNotBlank(c.Id)) {
//System.debug('El contacto tiene id: '+c.Id);
contactIdToCareerPathMap.put(c.Id, c.Currency__c);
}
}
List<Career_Path__c> careerPathToUpdate = [SELECT Id, Next_Currency__c, Contact__c
FROM Career_Path__c
WHERE Contact__c IN :contactIdToCareerPathMap.keySet()
];
if(careerPathToUpdate.size() > 0){
//System.debug('Tiene hijos');
for (Career_Path__c career : careerPathToUpdate) {
//System.debug('hijos: '+ career.id);
career.Next_Currency__c = contactIdToCareerPathMap.get(career.Contact__c);
}
update careerPathToUpdate;
}
}
You're trying to set trigger on Contact when you actually need to set it on Career_Path__c SObject. Your code should look something like this to achieve Next_Currency__c auto-populated from related Contact Currency__c (also take a note that trigger will now be BEFORE insert, so you do not need to update record again):
trigger Trigger_dynPicklist_2 on Career_Path__c (before insert) {
Set<Id> relatedContactsIds = new Set<Id>();
for (Career_Path__c path : Trigger.new) {
if (path.Contact__c != null) {
relatedContactsIds.add(path.Contact__c);
}
}
Map<Id, Contact> relatedContactsMap = [SELECT Id, Currency__c FROM Contact WHERE Id IN :relatedContactsIds AND Currency__c != NULL];
for (Career_Path__c path : Trigger.new) {
if (path.Contact__c != null && relatedContactsMap.containsKey(path.Contact__c)) {
path.Next_Currency__c = relatedContactsMap.get(path.Contact__c).Currency__c;
}
}
}
Also note, that when Contact is just created (and you are in trigger on insert), it cannot have related Career_Path__c objects.

Salesforce triggering Contact (PA) owner change when Account Owner is changed fails if the previous owners of the Contacts (PA) aren't the same user

I have a setup were we have Accounts and Person Accounts. The Person Accounts are in some cases linked to Accounts via AccountContactRelation. If the owner of a special record type of Accounts changes we want to change the owner of all related Person Accounts to the same owner as the new Account owner.
I've written an APEX trigger for this and if the current owners of all related Person Accounts are the same user, everything works perfectly. On the other hand if the some of the Person Accounts have different user/s as owners then the trigger fails with the error message:
ArmAccountOwnerChange: execution of BeforeUpdate, caused by: System.DmlException: Update failed. First exception on row 0 with id 0010Q0000050J0FQAU; first error: INVALID_FIELD, All accounts must have the same current owner and new owner.: [OwnerId] Trigger.ArmAccountOwnerChange: line 23, column 1
Here is the trigger. Any idea why this is happening?
trigger ArmAccountOwnerChange on Account (before update) {
List<AccountContactRelation> acr = New List<AccountContactRelation>();
List<Account> acc = New List<Account>();
RecordType rec = [Select Id From RecordType Where DeveloperName = 'ARM_Account' Limit 1];
for(Account a: Trigger.new) {
if(a.OwnerId != Trigger.oldMap.get(a.id).OwnerId && a.RecordTypeId == rec.id) {
acr = [SELECT Id, ContactId From AccountContactRelation Where AccountId = :a.id];
If(acr.size() > 0) {
for(AccountContactRelation b: acr) {
acc.addAll([Select Id, OwnerId From Account Where PersonContactId = :b.ContactId]);
}
}
If(acc.size() > 0) {
for(Account c: acc) {
c.OwnerId = a.OwnerId;
}
}
update acc;
}
}
}
You should set up an account list for each equal owner and then perform the update for each of this list.

How to create primary contact in account contact role for an account without duplicates

Whenever I'm trying to insert a new multiple contact for single account using dataloader, one primary contact should be inserted in account contact role object.
If multiple contacts are there we should consider first contact which is going to be inserted considered as primary contact remaining should not be inserted only one primary contact should be there in Account Contact Role object. In the below code, I have tried, primary contact is not getting created for an account.
trigger AccountContactRole on Contact(After insert,After Update){
list<AccountContactRole> acr=new list<AccountContactRole>();
set<ID> getid=new set<ID>();
Contact[] clist=trigger.new;
for(Contact Con : clist) {
if(con.AccountId!=Null) {
getid.add(Con.AccountId);
}
}
List<AccountContactRole> ac = [Select Id,AccountId,ContactId,IsPrimary from AccountContactRole Where AccountId in :getid and IsPrimary =True];
set<ID>accountid=new set<Id>();
for(AccountContactRole acv:ac) {
accountid.add(acv.AccountId);
}
System.debug('Records on List are '+ac +' And Records are '+ac);
for(Contact Cont: clist) {
AccountcontactRole C = new AccountcontactRole();
System.debug('Cont current value is'+Cont);
if(ac.isempty()) {
if(getid.contains(Cont.AccountId)==false){
C.contactId=Cont.Id;
C.Role='Decision Maker';
C.AccountId=Cont.AccountId;
C.Isprimary=true;
acr.add(C);
}
}
}
Insert acr;

Salesforce Trigger: Update field Trigger from Custom Object

New to apex and have a question about writing triggers. Essentially, I'm trying to create a trigger that updates a given field when another field is updated (after a record is created/inserted into Salesforce).
More specifically, when a custom Account lookup field (lookup value is a custom object record) is updated, it should update another field with a different value from the custom object record.
i.e. When I update the High School name to Elm High School, it will also update the yield associated with that high school.
Below is the code that I've created so far:
trigger UpdateYield on Account (before update) {
Set<String> HS = new Set<String>();
for (Account hsName : Trigger.new) {
if (hsName.High_School_LU__c != null) {
HS.add(hsName.High_School_LU__c);
}
List<High_School__c> y = [SELECT Name, Yield__c FROM High_School__c WHERE Name IN :HS];
Map<String, High_School__c> yLU = new Map<String, High_School__c>();
for (High_School__c h : y) {
yLU.put(h.Name, h);
}
for (Account YieldU : Trigger.new) {
if (YieldU.High_School_LU__c != null) {
High_School__c a = yLU.get(YieldU.Name);
if (a != null) {
YieldU.Yield__c = a.Yield__c;
}
}
}
}
}
It saves however, it still does not work when I update the field.
Any help would be greatly appreciated. Thanks in advance.
The problem here is that the value of a lookup field is not actually a string (as displayed in the UI) but an ID, so when you perform your SOQL query you are comparing an ID to the Name field and getting no results.
If you change your code to the following you should get the result you expect.
It should also be notified that this simple use case could also be accomplished using a simple Workflow Field Update rather than a trigger.
trigger UpdateYield on Account (before update)
{
Set<Id> HS = new Set<Id>();
for (Account hsName : Trigger.new)
{
if (hsName.High_School_LU__c != null)
{
HS.add(hsName.High_School_LU__c);
}
}
Map<Id, High_School__c> y = [SELECT Name, Yield__c FROM High_School__c WHERE Id IN :HS];
for (Account YieldU : Trigger.new)
{
High_School__c a = y.get(YieldU.High_School_LU__c);
if (a != null)
{
YieldU.Yield__c = a.Yield__c;
}
}
}