How do I convert a trigger to an Apex class - triggers

I need to transfer the below trigger logic to an apex class. But in apex class I cant use Trigger.New as it will give error. Once I give the logic in apex call, I can call the method in trigger so, Can anybody please tell me that how can I convert this trigger to an apex class?
trigger updatecontactrolecount on Opportunity(before insert, before update) {
Boolean isPrimary;
Integer iCount;
Map < String, Opportunity > oppty_con = new Map < String, Opportunity > (); //check if the contact role is needed and add it to the oppty_con map
for (Integer i = 0; i < Trigger.new.size(); i++) {
oppty_con.put(Trigger.new[i].id,
Trigger.new[i]);
}
isPrimary = False;
for (List < OpportunityContactRole > oppcntctrle: [select OpportunityId from OpportunityContactRole where(OpportunityContactRole.IsPrimary = True and OpportunityContactRole.OpportunityId in : oppty_con.keySet())]) {
if (oppcntctrle.Size() > 0) {
isPrimary = True;
}
}
iCount = 0;
for (List < OpportunityContactRole > oppcntctrle2: [select OpportunityId from OpportunityContactRole where(OpportunityContactRole.OpportunityId in : oppty_con.keySet())]) //Query for Contact Roles
{
if (oppcntctrle2.Size() > 0) {
iCount = oppcntctrle2.Size();
}
}
for (Opportunity Oppty: system.trigger.new) //Check if roles exist in the map or contact role isn't required
{
Oppty.Number_of_Contacts_Roles_Assigned__c = iCount;
Oppty.Primary_Contact_Assigned__c = isPrimary;
}
}

You can use a TriggerHandler Class to manage your all your triggerred events in the same place:
https://developer.salesforce.com/page/Trigger_Frameworks_and_Apex_Trigger_Best_Practices
In your trigger call your unique class :
trigger OpportunityTrigger on Opportunity (before insert,after insert,before update,after update, before delete,after delete) {
try {
new OpportunityTriggerHandler().run();
}
catch(Exception e) {
System.debug(e);
}
Then do all the logic in the TriggerHandler :
public with sharing class OpportunityTriggerHandler extends TriggerHandler {
private Map<Id, Opportunity> newOpportunityMap;
private Map<Id, Opportunity> oldOpportunityMap;
private List<Opportunity> newOpportunity;
private List<Opportunity> oldOpportunity;
public OpportunityTriggerHandler() {
this.newOpportunityMap = (Map<Id, Opportunity>) Trigger.newMap;
this.oldOpportunityMap = (Map<Id, Opportunity>) Trigger.oldMap;
this.newOpportunity = (List<Opportunity>) Trigger.new;
this.oldOpportunity = (List<Opportunity>) Trigger.old;
}
public override void beforeInsert() {
for (Opportunity o : this.newOpportunity) {
if (o.Name == '...') {
//do your stuff
}
}
}
public override void afterInsert() {
//...
}
}

Related

Trigger on a certain Account Record Type

So i have written a trigger that works perfectly fine and does exactly what i want it to do, but the thing is that it does the job on all the account, and i want it to work only on one record type of the accounts.
Can someone tell me what to add to my trigger so i can make it work only on one record type?
The following is my handler class:
public class AP03_OpportunityLineItem {
public static void preventmultipleOpportunityLineItems(List<OpportunityLineItem> listOppLineItems){
Set<Id>opportunityIds = new Set<Id>();
// get all parent IDs
for(OpportunityLineItem oli : listOppLineItems)
{
//Condition to pick certain records
opportunityIds.add(oli.OpportunityId);
}
// query for related Opportunity Line Items
Map<Id, Opportunity> mapOpportunities = new Map<Id, Opportunity>([SELECT ID,
(SELECT ID
FROM OpportunityLineItems)
FROM Opportunity
WHERE ID IN :opportunityIds]);
// opp counter of new records
Map<Id, Integer> mapOppCounter = new Map<Id, Integer>();
for(OpportunityLineItem oli : listOppLineItems)
{
if(mapOppCounter.containsKey(oli.OpportunityId))
{
mapOppCounter.put(oli.OpportunityId, mapOppCounter.get(oli.OpportunityId)+1);
}
else
{
mapOppCounter.put(oli.OpportunityId, 1);
}
}
//loop to add error if condition violated
for(OpportunityLineItem olitems : listOppLineItems)
{
if(mapOpportunities.get(olitems.OpportunityId).OpportunityLineItems.size()+mapOppCounter.get(olitems.OpportunityId)>1 || olitems.Quantity > 1)
{
olitems.addError('Ce client peut seulement loué un seul véhicule.');
}
}
}
}
The following is my PAD class:
public class PAD
{
public static String bypassTrigger; //List of bypassed triggers
public static final User user;
static {
user = [Select BypassApex__c
from User
where Id=:UserInfo.getUserId()];
bypassTrigger = ';'+ user.BypassApex__c+ ';';
System.debug('>>>>> PAD constructor : END <<<<<'+bypassTrigger);
}
/**
* Method used for the class PAD
* #param c object of type JonctionServicePrestation__c
* #return boolean
*/
public static boolean canTrigger(string Name){
return (bypassTrigger.indexof(';' + Name + ';') == -1);
}
}
And the following is my Trigger:
trigger OpportunityLineItemBeforeInsert on OpportunityLineItem (before insert) {
if(PAD.canTrigger('AP03_OpportunityLineItem')){
AP03_OpportunityLineItem.preventmultipleOpportunityLineItems(Trigger.new);
}
}
You would need to loop through your opportunitiesproducts and build a list of opportunity Id, then query the Opportunity whose Accounts in the list with the record type you want to match, and build a set of the ids that match the specified record type then check if the set contains the accountId of the opportunity being process to know if the skip or process it.
Set<Id> recordTypeOpp = new Set<ID>();
SET<Id> opportunityIds = new Set<Id>();
Id recordTypeIdYouWant = Schema.SObjectType.Account.getRecordTypeInfosByName().get('Record Type Name').getRecordTypeId();
for(OpportunityLineItem item : listOppLineItems){
opportunityIds.add(item.OpportunityId);
}
for(Opportunity item : [SELECT Id FROM Opportunity WHERE opportunityIds IN :opportunityIds and Account.RecordTypeId = :recordTypeIdYouWant]){
recordTypeOpp.add(item.Id);
}
for(OpportunityLineItem olitems : listOppLineItems)
{
if(recordTypeOpp.contains(olitems.OpportunityId)){
//do processing
}
else {
continue;
}
}

Writing Test class for Currency Conversion Trigger

I need little assistance in writing a test class for this trigger that converts the record currency to org currency. Can anyone please assist/guide? Please.
trigger convertToEuro on CustomObject(before update) {
List<CurrencyType> currencyTypeList = [select id,IsoCode,ConversionRate from CurrencyType where isActive = true] ;
Map<String , Decimal> isoWithRateMap = new Map<String, Decimal>();
for(CurrencyType c : currencyTypeList) {
isoWithRateMap.put(c.IsoCode , c.ConversionRate) ;
}
for(CustomObject ce: trigger.new){
if(ce.CurrencyIsoCode != 'EUR' && isoWithRateMap.containsKey(ce.CurrencyIsoCode)){
ce.Amount_Converted__c = ce.ffps_iv__Amount__c/ isoWithRateMap.get(ce.CurrencyIsoCode);
}
}
}
#isTest(seealldata=false)
public class testConvertToEuro{
#testSetup
static void setupTest(){
List<CurrencyType> liCT = New List<CurrencyType>();
List<CustomObject> liCO = New List<CustomObject>();
Integer iCounter = 0;
liCT.add(new CurrencyType(IsoCode='EUR',ConversionRate=1,isActive=TRUE));
liCT.add(new CurrencyType(IsoCode='USD',ConversionRate=2,isActive=TRUE));
liCT.add(new CurrencyType(IsoCode='XXX',ConversionRate=3,isActive=TRUE));
liCT.add(new CurrencyType(IsoCode='YYY',ConversionRate=4,isActive=TRUE));
liCT.add(new CurrencyType(IsoCode='ZZZ',ConversionRate=5,isActive=TRUE));
for(Integer i=0; i<251; i++){
CustomObject co = New CustomObject(
CurrencyIsoCode = liCT[iCounter].IsoCode;
ffps_iv__Amount__c = liCT[iCounter].ConversionRate + .01;
);
if(iCounter < liCT.size() -1){
iCounter += 1;
}
else{
iCounter = 0;
}
}
insert liCT;
insert liCO;
}
static testmethod void runTest(){
setupTest();
List<CustomObject> liCustomObjectsToUpdate = New List<CustomObject>();
for(CustomObject co : [SELECT Id, ffps_iv__Amount__c, Amount_Converted__c from CustomObject]){
co.ffps_iv__Amount__c -= .01;
liCustomObjectsToUpdate.add(co);
}
test.startTest();
Update liCustomObjectsToUpdate;
test.stopTest();
System.assertEquals([SELECT Id, Amount_Converted__c FROM CustomObject LIMIT 1][0].Amount_Converted__c, 1);
}
}
I can't test this code without adding your custom object so it might have some errors. Please let me know what errors you are getting. If you want help debugging, you should provide the exact code being used and the exact error message.
It follows the basic pattern for testing update triggers:
Insert test data
Retrieve test data
Update records
Assert that the trigger generated the desired result

Trigger on contentdocumentlink on insert, makes checkbox true on account

Trying to write a trigger on contentdocumentlink, which fires on insert and check a check box to true on account, but code doesn't seems to work.
Also getting compile error.
public with sharing class contentDocumentLinkTriggerHandler {
public static void processOnInsert(list<ContentDocumentLink> newList) {
list<ContentDocumentLink> contentList = new list<ContentDocumentLink>();
set<id> accId = new set<id>();
map<id,list<ContentDocumentLink>> parentContentMap = new map<id,list<ContentDocumentLink>>();
set<id> contId = new set<id>();
for(ContentDocumentLink nt : newList){
if(nt.linkedentityid.getSobjectType() == Account.SObjectType){
accId.add(nt.linkedentityid);
}
if(nt.linkedentityid.getSobjectType() == Contact.SObjectType){
contId.add(nt.linkedentityid);
}
if(parentContentMap.containsKey(nt.linkedentityid))
parentContentMap.get(nt.linkedentityid).add(nt);
else
parentContentMap.put(nt.linkedentityid,new list<ContentDocumentLink>{nt});
}
if(!accId.isEmpty()){
//login for acc
for(Account acc : [SELECT id,checkFiles__c FROM Account WHERE id IN : accId]){
if(parentContentMap.containsKey(acc.id)){
acc.checkFiles__c == true;// compile error
}
}
}
}
}

Trigger on Account to update Contact

Account and Contact both have Billing_Address__c field. Contact also have checkbox called active__c. If active__c is true and Account Billing_Address__c is update, then Contact's Billing_Address__c is updated . Here is the trigger. It is working fine. But I want to know if there is any problem or how can I optimize this in the terms of memory?
public static void updateContactBillingAddress(List<Account> lstNew, Map<Id,Account> mapOld){
Set<Id> accIds = new Set<Id>();
for(Account acc : lstNew){
if(acc.Billing_Address__c != mapOld.get(acc.Id).Billing_Address__c && acc.Billing_Address__c !=null){
accIds.add(acc.Id);
}
}
if(!accIds.isEmpty()){
List<Contact> lstContact = new List<Contact>([Select ID,active__c, Account.Billing_Address__c,Billing_Address__c FROM Contact where AccountID IN :accIds]);
List<Contact> lstUpdateCon = new List<Contact>();
for(Contact con : lstContact){
if(con.active__c == true){
if(con.Billing_Address__c != con.Account.Billing_Address__c){
con.Billing_Address__c = con.Account.Billing_Address__c;
lstUpdateCon.add(con);
}
}
else{
con.Billing_Address__c =null;
lstUpdateCon.add(con);
}
}
if(!lstUpdateCon.isEmpty()){
update lstUpdateCon;
}
}
}
Not really, it's semantics but i'd return a contact, 1 method, 1 thing. You're processing accounts and updating them, I'd return the List of contacts and not update them in the same method. If you need to update contacts down the road you will end up doing unnecessary DML statements, you also don't need to create a List just for that loop.
public static List<Contact> updateContactBillingAddress(List<Account> lstNew, Map<ID,Account> mapOld)
{
List<Contact> result = new List<Contact>();
Set<Id> accIds = new Set<Id>();
for(Account acc : lstNew)
{
if(acc.Billing_Address__c != null && acc.Billing_Address__c != mapOld.get(acc.Id).Billing_Address__c)
{
accIds.add(acc.Id);
}
}
if(!accIds.isEmpty())
{
for(Contact con : [Select ID,Active__c, Account.Billing_Address__c,Billing_Address__c FROM Contact where AccountID IN :accIds])
{
if(con.Active__c == true){
if(con.Billing_Address__c != con.Account.Billing_Address__c)
{
con.Billing_Address__c = con.Account.Billing_Address__c;
result.add(con);
}
}
else
{
con.Billing_Address__c = null;
result.add(con);
}
}
}
return result;
}

Is EntityReference.Load checking for EntityReference.IsLoaded?

Hi I was wondering if EntityReference.Load method includes
If Not ref.IsLoaded Then ref.Load()
My question is basically:
Dim person = Context.Persons.FirstOrDefault
person.AddressReference.Load()
person.AddressReference.Load() 'Does it do anything?
It does Load again. I verified this by Profiler and it shown two queries. Default merge option is MergeOption.AppendOnly and it doesn't prevent from querying again. Code from Reflector:
public override void Load(MergeOption mergeOption)
{
base.CheckOwnerNull();
ObjectQuery<TEntity> query = base.ValidateLoad<TEntity>(mergeOption, "EntityReference");
base._suppressEvents = true;
try
{
List<TEntity> collection = new List<TEntity>(RelatedEnd.GetResults<TEntity>(query));
if (collection.Count > 1)
{
throw EntityUtil.MoreThanExpectedRelatedEntitiesFound();
}
if (collection.Count == 0)
{
if (base.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One)
{
throw EntityUtil.LessThanExpectedRelatedEntitiesFound();
}
if ((mergeOption == MergeOption.OverwriteChanges) || (mergeOption == MergeOption.PreserveChanges))
{
EntityKey entityKey = ObjectStateManager.FindKeyOnEntityWithRelationships(base.Owner);
EntityUtil.CheckEntityKeyNull(entityKey);
ObjectStateManager.RemoveRelationships(base.ObjectContext, mergeOption, (AssociationSet) base.RelationshipSet, entityKey, (AssociationEndMember) base.FromEndProperty);
}
base._isLoaded = true;
}
else
{
base.Merge<TEntity>(collection, mergeOption, true);
}
}
finally
{
base._suppressEvents = false;
}
this.OnAssociationChanged(CollectionChangeAction.Refresh, null);
}
Just for reference for anyone else finding the accepted answer, here is the extension method I created for my current project.
using System.Data.Objects.DataClasses;
namespace ProjectName
{
public static class EntityFrameworkExtensions
{
public static void EnsureLoaded<TEntity>(this EntityReference<TEntity> reference)
where TEntity : class, IEntityWithRelationships
{
if (!reference.IsLoaded)
reference.Load();
}
}
}
And usage:
Patient patient = // get patient
patient.ClinicReference.EnsureLoaded();
patient.Clinic.DoStuff();