I am trying to write a trigger to update the 'Name' field (so WhoId in the API) to the 'Name' (ContactId) of the custom Primary Contact field of the Account the Task is related to.
trigger updateNameToPrimary on Task (after insert, after update) {
for(Task t : Trigger.new) {
t.WhoID = [SELECT Account.Id
FROM Account
WHERE Id = :t.Id].Custom_Primary_Contact__c;
}
}
I have been doing some testing and don't think it's working and cannot figure out why. Just looking for a point in the right direction as I still am in the learning process.
Change Trigger from "after insert after update" To "before insert before update".
Something like this
trigger updateNameToPrimary on Task (before insert, before update) {
set<Id> accIdSet = new set<Id>();
for(Task t : Trigger.new) {
if(t.AccountId!=null)
accIdSet.add(t.AccountId);
}
map<Id,Account> accMap = new map<Id,Account>([select Custom_Primary_Contact__c from Account where Id in:accIdSet]);
for(Task t : Trigger.new) {
if(t.AccountId!=null && accMap.containsKey(t.AccountId))
t.WhoID = accMap.get(t.AccountId).Custom_Primary_Contact__c;
}
}
Related
I have a custom object "Task Tracking" with 3 custom fields:
1) Lookup to user object.
2) Number of open task (number field)
3) Number if closed task (number field)
When a task is created by user A with let's stay status is IN PROGRESS, I need to create record in Task Tracking object with these details :
1) Lookup field = user A
2) Number of open task = 1
3) Number of closed task = 0;
Now next time, when same user A creates another task, the new Task Tracking record shouldn't be created but it should only update Number of task field.
I have tried this much. I was able to create Task Tracking record whenever a Task is created but It was creating new Task Tracking Object for every Task that I am creating
trigger TrackTask2 on Task (before insert) {
List<sujya__Task_Tracking__c> li = new List<sujya__Task_Tracking__c>();
sujya__Task_Tracking__c s = new sujya__Task_Tracking__c();
if(Trigger.isBefore && Trigger.isInsert){
for(Task t:Trigger.new){
s.sujya__User__c = t.CreatedById;
li.add(s);
}
insert li;
}
}
You should query the system first to see if one exists. If it does, then update it, if it does not then create one.
trigger TrackTask2 on Task (before insert) {
Set<Id> users = new Set<Id>();
for (Task t : Trigger.new) {
users.add(t.ownerId); //might be t.whoId;
}
List<sujya__Task_Tracking__c> existingTrackers = [SELECT Id, sujya__User__c, number_open_task__c, number_closed_task__c FROM sujya__Task_Tracking__c WHERE sujya__User__c IN :users];
Map<Id, sujya__Task_Tracking__c> userTrackerMap = new Map<Id, sujya__Task_Tracking__c>();
for (sujya__Task_Tracking__c tracker : existingTrackers) {
userTrackerMap.put(tracker.sujya__User__c, tracker);
}
for (Task t : Trigger.new) {
sujya__Task_Tracking__c userTracker = existingTrackers.get(t.ownerId);
if (userTracker == null) {
userTracker = new sujya__Task_Tracking__c();
userTracker.number_open_task__c = 1;
userTracker.sujya__User__c = t.ownerId;
} else {
userTracker.number_open_task__c += 1;
}
userTrackerMap.put(userTracker.sujya__User__c, userTracker);
}
upsert userTrackerMap.getValues();
}
This will get you most of the way, but you're going to have to add logic for when a task is closed to decrement the number of open tasks and the number of close tasks.
And please follow trigger best practices by using a handler class
how to write a trigger on account object(salesfroce) if the existing record then update some fields according to you else create a new Records ?
Thanks
How about using upsert? It allows you to either insert record or update if it already exists. insert and update in a single, smart command.
You want to update record, and if it does not exist, create new one.
With upsert you can first try to obtain record (if nothing found, create new instance), then set your fields up, and finally, upsert it.
Like:
Account myAcc;
try {
myAcc = [ SELECT Id, Name FROM Account where Id='someIdImLookingFor'];
myAcc.Name = 'My desired name';
catch (Exception e) {
myAcc = new Account(Name='My desired name');
}
upsert myAcc
'Upsert' is the best way to do that. If the record is present it helps you to update the record else create a new one.
Account[] acctsList = [SELECT Id, Name, BillingCity FROM Account WHERE BillingCity = 'Bombay'];
for (Account a : acctsList) {
a.BillingCity = 'Mumbai';
}
Account newAcct = new Account(Name = 'Acme', BillingCity = 'San Francisco');
acctsList.add(newAcct);
try {
upsert acctsList;
} catch (DmlException e) {
// Process exception here
}
I have an external id in Account named Applicant_ID__c. I am using data loader to import data into salesforce Opportunity. Below is my mapping file content.
Date\ Cancelled=Date_Cancelled__c
Date\ Denied=Date_Denied__c
Date\ Decisioned=Date_Decisioned__c
App\ Status=Application_Status__c
Date\ Submitted=Application_Submitted__c
Closing\ Date=CloseDate
Application\ Source=Application_Source__c
Application\ Type=Application_Type__c
Application\ Sub-Type=Application_Sub_Type__c
App\ ID=App_ID__c
Property=Property_Name__r\:Property_Code__c
Applicant\ ID=Account\:Applicant_ID__c
Record\ Type\ ID=RecordTypeId
The above mapping is working correctly now what i want is to populate the opportunity name from trigger.
Below is my trigger content
trigger MapStatusToStageBeforeOpportunintyCreation on Opportunity (before insert, before update) {
for (Opportunity o : Trigger.New){
Account acc = [Select LastName From Account Where Applicant_ID__c =:Account:Applicant_ID__c];
o.Name =acc.LastName;
}
}
Thanks in advance.
That answer you created and excepted is going to blow up if insert 101 Opportunities, but if you want to use the Applicant_ID__c you can query it out in the account query
trigger MapStatusToStageBeforeOpportunintyCreation on Opportunity (before insert, before update)
{
Set<ID> acctIDS = new Set<ID>();
for (Opportunity o : Trigger.new)
{
if(o.AccountId != null)
{
acctIDS.add(o.AccountID);
}
}
Map<ID, Account> acctMap = new Map<ID, Account>([Select LastName, Applicant_ID__c From Account Where ID =: acctIDS]);
for(Opportunity o : Trigger.new)
{
for(ID acctID :acctMap.keySet())
{
if(o.AccountID == acctID)
{
o.Lastname = acctMap.get(acctID).LastName;
}
}
}
}
You are querying it wrong
First, you should Never Query in for loop
List'<'Opportuniy opplist = new list'<'Opportunity'>'();<Br/>
// Remove the single quotes <br/>
for (Opportunity o : Trigger.New){<Br/>
o.OpportunityApplicentID = o.Account.Applicant_ID__c;<Br/>
o.Name =acc.LastName;<Br/>
opplist.add(o);<Br/>
}
update opplist;
+Instead of using Applicant_ID__c =:Account:Applicant_ID__c this..Just use Applicant_ID__c =:Account.Applicant_ID__c.Don't use colon .Use dot operator
If possible, could anyone please direct me towards the right direction? The second code (Batch Apex) is just not compiling. Currently the error is,
Error: Compile Error: Invalid type: updateContactOnEmailOptOutChangeScheduler
at line 63 column 73
But I think there are other issues to that I can't seem to make it right.
On a contact update, the trigger updates all duplicate contacts with new value of "Email Opt Out" if this field has been updated. Also, the trigger only updates duplicate contacts that have a HasOptedOutOfEmail value different from one being updated. Now my task is to convert this requirement from trigger (that was written and tested by my colleague) to Batch Apex. First is the original trigger. Second is the code I just wrote in the format of batch apex.
Original Trigger Code
trigger updateContactOnEmailOptOutChange on Contact (after update) {
//Initialize lists and maps
List<Contact> duplicateContacts = new List<Contact>();
Map<String, Contact> contactEmailMap = new Map<String, Contact>();
Map<Id, Contact> contactIdMap = new Map<Id, Contact>();
//Build a map with contacts to update. Only select the ones that have a different "Email Opt Out" value from the contact being updated.
for (Integer i = 0; i < Trigger.new.size(); i++) {
if (Trigger.old[i].HasOptedOutOfEmail != Trigger.new[i].HasOptedOutOfEmail) {
contactEmailMap.put(Trigger.old[i].email, Trigger.new[i]);
contactIdMap.put(Trigger.old[i].id, Trigger.new[i]);
}
}
//Only go through this process if "Email Opt Out" (HasOptedOutofEmail) was updated.
If (contactIdMap.size()>0) {
//Query the database and look for all contacts with a duplicate email address (same email as the contact currently being updated).
for (Contact dupContact : [SELECT Id, Name, Email, HasOptedOutOfEmail
FROM Contact
WHERE Email IN : contactEmailMap.KeySet()
AND Id NOT IN : contactIdMap.KeySet()]) {
Contact contact = contactEmailMap.get(dupContact.Email);
If (dupContact.HasOptedOutOfEmail <> contact.HasOptedOutOfEmail) {
dupContact.HasOptedOutOfEmail = contact.HasOptedOutOfEmail;
duplicateContacts.add(dupContact);
}
}
//If any duplicate contacts were found, update all duplicate contacts with the new HasOptedOutOfEmail value.
If (duplicateContacts.size()>0) update duplicateContacts;
}
}
BATCH APEX
global class updateContactOnEmailOptOutChange implements Database.Batchable<sObject>
{
global string query;
global updateContactOnEmailOptOutChange()
{
query = 'SELECT id,Name, Email, HasOptedOutofEmail from Contact where HasOptedOutofEmail=true';
}
global Database.QueryLocator start(Database.BatchableContext BC)
{
return Database.getQueryLocator(query);
}
global void execute(Database.BatchableContext BC, List <sObject> duplicateContacts)
{
Map<String, Contact> contactEmailMap = new Map<String, Contact>();
Map <Id, Contact> contactIdMap = new Map<Id, Contact>();
// Build a map with contacts to update. Only select the ones that have a different "Email Opt Out" value from the contact being updated.
if(trigger.isUpdate){
for(Integer i=0; i<Trigger.new.size();i++)
{
if(Trigger.old[i].HasOptedOutOfEmail != Trigger.new[i].HasOptedOutOfEmail)
{
contactEmailMap.put(Trigger.old[i].email, Trigger.new[i]);
contactIdMap.put(Trigger.old[i].id, Trigger.new[i]);
}
}
if(contactidMap.size()>0)
{
//Query the database and look for all contacts with a duplicate email address(same email as the contact currently being updated)
for (Contact dupContact: [SELECT Id, Name, Email, HasOptedOutofEmail
FROM Contact
WHERE Email IN: contactEmailMap.KeySet()
AND Id NOT IN: contactIdMap.KeySet()])
{
Contact contact=contactEmailMap.get(dupContact.Email);
If(dupContact.HasOptedOutOfEmail <> contact.HasOptedOutOfEmail)
{
dupContact.HasOptedOutOfEmail = contact.HasOptedOutOfEmail;
duplicateContacts.add(dupContact);
}
}
// if any duplicate contacts were found, update all duplicate contacts with the new HasOptedOutOFEmail value.
If(duplicateContacts.size<>0) update duplicateContacts;
}
}
}
//The batch process has completed successfully. Schedule next batch.
global void finish(Database.BatchableContext BC){
// //Build the system time of now + 300 seconds to schedule the batch apex.
Datetime sysTime = System.now();
sysTime = sysTime.addSeconds(300);
String chron_exp=''+sysTime.second()+''+sysTime.minute()+''+sysTime.hour()+''+sysTime.day()+''+sysTime.month()+'?'+sysTime.year();
system.debug(chron_exp);
updateContactOnEmailOptOutChangeScheduler scheduleFieldUpdate = new updateContactOnEmailOptOutChangeScheduler();
//Schedule the next job, and give it the system time so name is unique
System.schedule('New Email Update Job'+sysTime.getTime(),chron_exp,scheduleFieldUpdate);
}
}
Your batch apex is including references to the Trigger class that is only valid inside a trigger. The compile error message says there is an invalid type on line 63. Check the line numbers but that likely points to the reference to Trigger
I wrote a trigger that places the Account Owner Name on a Case created for that Account. It works and performs also in my bulk test of 200. Here is the code:
trigger CaseBeforeInsertUpdate on Case (before insert, before update) {
Set<Id> accountIds = new Set<Id>();
Set<Id> accountOwnerIds = new Set<Id>();
for (Case c : Trigger.new) {
if(c.AccountId != null) {
accountIds.add(c.AccountId);
}
}
Map<Id,Account> accountMap = new Map<Id, Account>([select Id, OwnerId from Account where Id IN :accountIds]);
for (Account a : accountMap.values()) {
if(a.OwnerId != null) {
accountOwnerIds.add(a.OwnerId);
}
}
Map<Id, User> userMap = new Map<Id,User>([select Name from User where Id IN :accountOwnerIds]);
if(userMap.size() > 0) {
for(Case c: Trigger.new) {
c.MerchantOwner__c = userMap.get(accountMap.get(c.AccountId).OwnerId).name;
}
}
}
By accident I discovered a bug and I can't figure out what is going wrong. If I go to a Case listview (i.e. My open cases) and select multiple Cases and Close them, I get an error: System.NullPointerException: Attempt to de-reference a null object for the row that updates the MerchantOwner field. When I mass close cases in my testclass everything works fine..
My best guess is I'm trying to do this for a Case that has no Account attached to it but as far as I see I try to not have these Cases updated by not adding them to the accountIds Set in the first place.
Does anyone know what I am doing wrong? Any help would be greatly appreciated.
I would change the following For loop from:
for(Case c: Trigger.new) {
c.MerchantOwner__c = userMap.get(accountMap.get(c.AccountId).OwnerId).name;
}
To
for(Case c: Trigger.new) {
if (c.AccountId != null // Make sure there is an Account linked to the Case
&& accountMap.ContainsKey(c.AccountId) // Make sure our Account query captured it
&& accountMap.get(c.AccountId).OwnerId != null // Make sure that account has an owner
&& usermap.ContainsKey(accountMap.get(c.AccountId).Ownerid) // Finally make sure our User query found the owner
){
c.MerchantOwner__c = userMap.get(accountMap.get(c.AccountId).OwnerId).name;
}
}