Trigga What? A Simple Non-Profit Trigger Example

I do some consulting work for non-profit organizations that use Salesforce.com.  There are so many non-profits that use Salesforce but many often lack the internal resources necessary to truly realize the benefits of adopting it.  It is highly rewarding work and I would encourage you to check out the Salesforce Foundation as one way of getting involved with non-profits who use Salesforce.

The Salesforce Foundation has a very successful program that gives non-profits the ability to get 10 donated (free) Enterprise Edition user licenses. This is a great program that offers a huge benefit to non-profits.  If you’re a non-profit you can learn more about how to apply for your licenses here.  If you have experience in configuring, customizing, administering, consulting, and/or developing on the Force.com platform you can get involved by emailing foundation@salesforce.com and letting them know you’re interested in volunteering your time and expertise.  This is a great first step towards getting involved.

With that being said I’d like to share a specific real world example for an Apex Trigger that might be helpful.

Use Case

Here’s the scenario.

We use Linvio’s PaymentConnect app to process online donations that come through our website.  When someone pays online via a credit card a Payment object record is created.  Payment is a custom object included in the PaymentConnect package.  The Payment record captures information about the transaction including credit card info, payment gateway, donation amount, etc.  It also captures the name, email, and contact info of the donor who initiates the transaction.  With this contact info a Contact record is either created (if no match is found) or the Payment is related to an existing Contact (if a match is found).

The challenge we faced is that we track donations through Opportunities.  While the Payment record does capture the amount of the donation and relates it to a Contact, we really wanted an Opportunity to be created that reflects each payment because that is how we report on donation income, do forecasting, etc.  We also wanted to relate the Contact to this Opportunity through a Contact Role.

One option could have been to create a workflow rule,that fires everytime a payment is successfully processed, and sets a Task for someone to manually create a new opportunity and relate it to the Contact.  But we wanted to automate this process as much as possible so we decided to write a trigger.

Below is the code for the trigger and the Apex Class that handles it.  Keep in mind that we are using the NonProfitForce configuration.  You can read more about NonProfitForce here.

The main point of the trigger is to create a Closed Opportunity (Donation) each time a successful Payment is processed.  Then, the corresponding Contact is related to the Opportunity through an OpportunityContactRole.  This trigger is bulkified to handle multiple Payments without easily tripping governor limits.  Read up on trigger governor limits here.

I’d also highly recommend considering Linvio’s PaymentConnect app for your ecommerce and payment processing needs.  Ron Wild and his team are highly competent and helpful.

Trigger


trigger NewDonationAfterPayment on pymt__PaymentX__c (after insert) {
 PaymentManager.handleNewPayment(Trigger.New);
}

Apex Class

public with sharing class PaymentManager {

 public Class PaymentManagerException extends Exception {}

 public static void handleNewPayment(List <pymt__PaymentX__c> newPayments) {
 List <pymt__PaymentX__c> completedPayments = new List <pymt__PaymentX__c>();
 List <Opportunity> donationList = new List <Opportunity>();
 List <OpportunityContactRole> ocrList = new List <OpportunityContactRole>();

 // Loop through all new Payment objects in the trigger and add the ones with a 'Completed' status and Type of 'Payment'.
 for (pymt__PaymentX__c payment : newPayments) {
 if (payment.pymt__Status__c == 'Completed' && payment.pymt__Transaction_Type__c =='Payment') {
 completedPayments.add(payment);
 }
 } //close for-loop

 // If there are new Payments that fit our criteria above then move forward.
 if (completedPayments.size() > 0) {

 // Loop through the Payments that fit our criteria and create a new Opportunity that mirrors certain information from the Payment.
 for (pymt__PaymentX__c p : completedPayments) {
 Opportunity donation = new Opportunity();
 donation.Name = p.Name + ' - ' + p.pymt__Amount__c;
 donation.CloseDate = date.Today();
 donation.StageName = 'Posted';
 donation.Amount = p.pymt__Amount__c;
 donationList.add(donation);
 }

 // Insert these new Opportunities (called Donations in NonProfitForce)
 try {
 insert donationList;
 } catch (Dmlexception e) {
 System.debug('donationList not inserted: ' + e);
 }

 // Create two new Lists that will be used to relate the new Opportunities to the Payments in the trigger.
 // Select all the opportunities that we just created and order them by Name.  Then select all the Payments that fit our criteria from the trigger
 // and order them by Name.  Theoretically, these lists should be the same size and follow the same order since the Names mirror each other..
 List <Opportunity> oppList = new List <Opportunity>([Select Id, Name FROM Opportunity WHERE Id IN :donationList ORDER BY Name]);
 List <pymt__PaymentX__c> pymtList = new List <pymt__PaymentX__c> ([Select Id, Name FROM pymt__PaymentX__c WHERE Id IN :completedPayments ORDER BY Name]);

 //  This loop will assign the opportunity id to the Payment object.
 for (Integer i=0; i < pymtList.size(); i++) {
 pymtList[i].pymt__Opportunity__c = oppList[i].Id;
 }

 // Update the list of Payments from above, this should save the Opportunity Id that we just added.
 try {
 update pymtList;
 } catch (Dmlexception e) {
 System.debug('pymtList not inserted: ' + e);
 }

 // Create a new list of Payment objects so we can relate the Contacts to the Opportunities that we just created.
 //Since the Payment object is related to both the Contact and Opportunity we can query those Id's directly from the Payment object.
 List <pymt__PaymentX__c> listForContRole = new List <pymt__PaymentX__c> ([Select Id, pymt__Contact__r.Id, pymt__Opportunity__r.Id FROM pymt__PaymentX__c WHERE ID IN :completedPayments]);
 List <OpportunityContactRole> contRoles = new List<OpportunityContactRole>();

 // Loop through this new list of Payment objects.  Create a new OpportunityContactRole and using the Opp and Contact Id's from the Payment object.
 for (pymt__PaymentX__C p : listForContRole) {
 OpportunityContactRole ocr = new OpportunityContactRole();
 ocr.ContactId = p.pymt__Contact__r.Id;
 ocr.OpportunityId = p.pymt__Opportunity__r.Id;
 contRoles.add(ocr);
 }

 // Insert this new list of OpportunityContactRoles
 try {
 insert contRoles;
 } catch (DmlException e) {
 System.debug('ocrList not inserted: ' + e);
 }

 } //close if-statement

 } //close handleNewPayment method

} //close PaymentManager Class

I hope this is helpful, and as always look forward to your feedback.

Tags: , ,

No comments yet.

Leave a Reply