Connecting Unrelated Objects during Apex Trigger - triggers

I am trying to create a trigger that does the following:
Once an Account has been created, create an unrelated record (called a "Portal Content" record) that bears the same name, assuming the default RecordTypeId
Take the ID of the newly created "Portal Content" record, and insert it into a lookup field on the originally created Account
Add the ID of the original Account, and enter it into a field of the newly created "Portal Content" record
Steps 1 and 2 were addressed in post Populate Lookup Field with Record Created From Trigger. The new problem is that when attempting Item 3, in the Trigger.isAfter code block, a.Portal_Content_Record__r returns null rather than with the Id value populated after insert p in the Trigger.isBefore block.
trigger newAccountCreated on Account (before insert, after insert) {
List<Account> alist = Trigger.New;
if(Trigger.isBefore){
for(Account a : alist) {
if (a.RecordTypeId == '012i0000001Iy1H') {
Portal_Content__c p = new Portal_Content__c(
Name=a.Name,
RecordTypeId='012i0000001J1zZ'
);
insert p;
a.Portal_Content_Record__c = p.Id;
system.debug('Made it to insert p. P = ' + p.Id +'. a.Portal_Content_Record__c = ' + a.Portal_Content_Record__c);
}
}
}
if (Trigger.isAfter) {
for(Account a : alist){
system.debug('a.Id = ' + a.Id + ', p = ' +a.Portal_Content_Record__r);
String p = a.Portal_Content_Record__c;
for(Portal_Content__c port : [SELECT ID FROM Portal_Content__c WHERE Id = :p]){
port.School_SFDC_ID__c = a.Id;
update port;
}
}
}
}
My question has two parts:
How do I assign a field on the newly inserted Portal_Content__c record with the ID of the Account that started the trigger?
Can this be done within this trigger, or is a secondary "helper" trigger needed?

I was able to figure out a way to answer this issue within the same Trigger. The Trigger.isAfter && Trigger.isInsert code block is what solved my problem.
trigger newAccountCreated on Account (before insert, after insert, after delete) {
List<Account> alist = Trigger.New;
List<Account> oldlist = Trigger.old;
if(Trigger.isBefore){
for(Account a : alist) {
if (a.RecordTypeId == '012i0000001Iy1H') {
Portal_Content__c p = new Portal_Content__c(
Name=a.Name,
RecordTypeId='012i0000001J1zZ'
);
insert p;
a.Portal_Content_Record__c = p.Id;
}
}
}
else if (Trigger.isAfter && Trigger.isDelete){
for(Account a : oldlist){
for(Portal_Content__c p : [SELECT ID FROM Portal_Content__c WHERE ID = :a.Portal_Content_Record__c]){
delete p;
}
}
}
if (Trigger.isAfter && Trigger.isInsert){
for(Account a : alist){
List<Portal_Content__c> plist = [SELECT ID FROM Portal_Content__c WHERE Id = :a.Portal_Content_Record__c];
for(Portal_Content__c p : plist){
p.School_SFDC_ID__c = a.Id;
update p;
}
}
}
}
This code block does a query for Portal_Contact__c records that match the Account record's Portal_Content_Record__c field value, which is assigned in the first code block. It then takes the Portal_Content__c records found, and assigns the original Account's Id to the record's School_SFDC_ID__c field value.

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.

Access Trigger.New value in Apex Test Class

tl;dr - How do you make a test class wait until a specific trigger is done with its updates or inserts before continuing on with the test?
Background
I have a trigger that creates a new Portal_Content__c object whenever an Account object is created, and then associates both records with each other (see Account Trigger below).
I have also created a trigger on the Portal_Content__c object that fires when a record is deleted. When deleted, the trigger looks up the related Account record and deletes it (see Portal Content Trigger below).
My question is regarding my Portal Content Test Class (final code block below). When I run a DML insert on an Account record, the Account Trigger should populate the a.Portal_Content_Record__c field with the Portal Content record created in the Account Trigger. However, when I ran my system.debug() tests, it showed that the a.Portal_Content_Record__c field is null when the SOQL query is run. I have been able to bypass this by doing a search for the Portal Content record by matching name, but I would like to make the script more robust by searching by the exact ID.
Question
How do I make the Portal Content Test Class wait until the Account Trigger has populated the a.Portal_Content_Record__c field before moving on with the test?
--Code Blocks--
Account Trigger
trigger AccountHandler on Account (before insert, after insert, after update, after delete) {
List<Account> alist = Trigger.New;
List<Account> oldlist = Trigger.Old;
// Create new Portal Content with same name as Account if Account is record type 'College/University,'
// then assign newly created Portal Content to 'Portal Content Record' lookup field on new Account
if(Trigger.isBefore && Trigger.isInsert){
for(Account a : alist) {
if (a.RecordTypeId == '012i0000001Iy1H') {
Portal_Content__c p = new Portal_Content__c(
Name=a.Name,
RecordTypeId='012i0000001J1zZ'
);
insert p;
a.Portal_Content_Record__c = p.Id;
}
}
}
// Delete Portal Content record referenced in an Account's 'Portal Content Record' lookup field
// if the Account is deleted
if (Trigger.isAfter && Trigger.isDelete){
for(Account a : oldlist){
for(Portal_Content__c p : [SELECT ID FROM Portal_Content__c WHERE ID = :a.Portal_Content_Record__c]){
delete p;
}
}
}
// After the new Portal Content record has been created, assign the Account ID of the Account that created it
// to the 'School SFDC ID' field on the new Portal Content record.
if (Trigger.isAfter && Trigger.isInsert){
for(Account a : alist){
List<Portal_Content__c> plist = [SELECT ID FROM Portal_Content__c WHERE Id = :a.Portal_Content_Record__c];
for(Portal_Content__c p : plist){
p.School_SFDC_ID__c = a.Id;
update p;
}
}
}
// Prevent more than one Account from being assigned to a single Portal Content record
if (Trigger.isAfter && Trigger.isUpdate) {
for(Account a : alist){
if (a.Portal_Content_Record__c != null){
List<Account> alist = [SELECT ID FROM Account WHERE Portal_Content_Record__c = :a.Portal_Content_Record__c];
system.debug('alist: ' + alist);
if (alist.size() > 1) {
a.addError('The Portal Content record you selected is already associated with another School. Please select a different Portal Content record');
}
}
}
}
}
Portal Content Trigger
trigger PortalContentHandler on Portal_Content__c (before insert, after update, after insert, after delete) {
// If Portal Content is deleted and Account is tied to Account record, delete Account record
if(Trigger.isAfter && Trigger.isDelete){
List<Portal_Content__c> plist = Trigger.old;
for(Portal_Content__c p : plist) {
List<Account> alist = [SELECT ID FROM Account WHERE Id = :p.School_SFDC_ID__c];
for(Account a : alist){
delete a;
}
}
}
// If more than one Portal Content with the same name, prevent new Portal Content record from being created
else if(Trigger.isAfter && (Trigger.isUpdate || Trigger.isInsert)){
List<Portal_Content__c> plist = Trigger.New;
for(Portal_Content__c p : plist){
List<Portal_Content__c> pquery = [SELECT ID FROM Portal_Content__c WHERE Name = :p.Name];
if(pquery.size() > 1){
p.addError('There is already a Portal Content record with this name. Please select a different name.');
}
}
}
}
Portal Content Test Class
#isTest
public class PortalContentHandlerTest {
#isTest static void createThenDeletePortalContent(){
Account a = new Account(Name='deletePortalTest',RecordTypeId='012i0000001Iy1H');
insert a;
List<Portal_Content__c> plist = [SELECT ID FROM Portal_Content__c WHERE Name = :a.Name];//Id = :a.Portal_Content_Record__c];
system.debug('Delete Info: a.Id = ' + a.Id + ', Portal_Content_Record = ' + a.Portal_Content_Record__c+ ', plist = ' + plist);
for(Portal_Content__c p : plist){
delete p;
}
system.debug('Delete Info: a.Id = ' + a.Id);
List<Account> checklist = [SELECT ID FROM Account WHERE Id = :a.Id];
system.debug(checklist);
system.assertEquals(0, checklist.size());
}
I have tested it in my Org. Please find my comments below.
According to your requirement, I have a trigger that creates a new Portal_Content__c object whenever an Account object is created, and then associates both records with each other (see Account Trigger below).
I see there is a relationship on the Account(Portal_Content_Record__c ) and the Portal Content (School_SFDC_ID__c). You are setting the Portal_Content_Record__c. but you need to link the School_SFDC_ID__c as well to fulfill your requirement. Please find the below code
trigger AccountHandler on Account (before insert, after insert, after update, after delete) {
List<Account> alist = Trigger.New;
List<Account> oldlist = Trigger.Old;
// Create the new Portal Content with the same name as Account if Account is record type College/University,
// then assign newly created Portal Content to Portal Content Record lookup field on new Account
if(Trigger.isBefore && Trigger.isInsert){
for(Account a : alist) {
if (a.RecordTypeId == '012i0000001Iy1H') {
Portal_Content__c p = new Portal_Content__c(
Name=a.Name,
RecordTypeId='012i0000001J1zZ',
School_SFDC_ID__c = a.id
);
insert p;
a.Portal_Content_Record__c = p.Id;
}
}
}
}
I see the only thing is delete is not getting covered in
PortalContentHandler because it doesn't meet with the condition for
List<Account> alist = [SELECT ID FROM Account WHERE Id =
:p.School_SFDC_ID__c]; Populate it when you are creating
Portal Content as describe in the above point.
Also, you need to change your test class as there is no data for Portal Content that you are trying to query. In the test class, you get the data what you created there only. The one which gets created by AccountHandler won't be part of your test class. Create the same data as you are creating in the trigger.
#isTest
public class PortalContentHandlerTest {
#isTest static void createThenDeletePortalContent(){
Account a = new
Account(Name='deletePortalTest',RecordTypeId__c='012i0000001Iy1H');
insert a;
Portal_Content__c p = new Portal_Content__c(
Name=a.Name,
RecordTypeId__c='012i0000001J1zZ',
School_SFDC_ID__c = a.id
);
insert p;
delete p;
}
}

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.

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;
}
}
}