Showing posts with label Apex Visualforce Salesforce triggers applications Developers SaaS classes Objects Apex Code. Show all posts
Showing posts with label Apex Visualforce Salesforce triggers applications Developers SaaS classes Objects Apex Code. Show all posts

Monday, 1 November 2010

Apex Trigger #2

I hope everyone enjoyed their Halloween weekend. Halloween weekend in San Francisco is amazing; as is every weekend I have spent here. Out of all the cities I have ever been to, east coast and west coast, San Francisco is hands down the most fun. That being said not everything is about my wild nights in the city; my time is primarily focused on becoming a better developer. This leads us into creating our 2nd Apex trigger…. (I’m really good at transitions)

We will utilize the Apex Code editor again to develop our second trigger.  Here is a repeat screen shot of what the editor looks like on the Salesforce page:



The trigger we are creating will maintain the invoice’s payment status based on the scheduled payments. Begin by opening the Scheduled Payment object definition by going to ‘Setup’, ‘Build’, then ‘Objects’. Scroll down to the triggers section and hit ‘New’. Replace the blank trigger template with the following code:

// This trigger scans the list of updated Scheduled Payments 
// and updates the invoice status if it has changed as a result
// of one of the payments becoming past due or a payment has
// been made.
trigger ScheduledPayment_After_Update on Scheduled_Payment__c
  ( after update )
{
    // Apex Code's maximum batch size for updates, inserts and
    // deletes. The final key word indicates a constant.
    final Integer MAX_BATCH_SIZE = 200; 
    List<Scheduled_Payment__c> statusChanges;
    List<Scheduled_Invoice__c> updateInvoices;
    List<Scheduled_Invoice__c> queryResults;
    Set<Id> invoiceIds = new Set<Id>();
    statusChanges = new List<Scheduled_Payment__c>();
    updateInvoices = new List<Scheduled_Invoice__c>();
    // Loop through the updated payments and see if any statuses changed.
    // If they did, add them to the statusChanges list.
    // The trigger object has two properties containing lists of updated
    // objects, new and old. For insert triggers only the new property
    // has values.
    for ( Integer x = 0; x < trigger.new.size(); x++ )
    {
        if ( trigger.new[x].Status__c != trigger.old[x].Status__c )
            statusChanges.add( trigger.new[x] );
    }
    // If there aren't any status changes, exit.
    if ( statusChanges.size() == 0 )
        return;
    // Loop through the list of updated payments and get all the
    // unique invoice IDs.
    // Adding the Ids to a set will ensure that no Ids are
    // duplicated within the list.
    for ( Scheduled_Payment__c payment : statusChanges)
    {
        invoiceIds.add( payment.Invoice__c );
    }
  
    // Query both the invoice object and the related
    // payment objects at once to retrieve their status.
    // Only query invoices in invoice ID set.
    // Querying child objects is similar to a correlated sub-query
    // but the join to the parent object is implicit.
    queryResults = [SELECT Id, Status__c, (SELECT Id, Status__c
        FROM Scheduled_Payments__r) FROM Scheduled_Invoice__c
        WHERE Id IN :invoiceIds];
  
    // Loop through the results and check the status of the
    // payments for the affected invoices and add the invoice
    // to the update list if the status needs changing.
    for ( Scheduled_Invoice__c invoice : queryResults )
    {
        String newStatus = 'Paid in Full';
        
        // Loop through the Scheduled Payment child objects
        // of this invoice, using the Master-Detail relationship
        // you defined previously. The relationship name has
        // "__r" appended to it to indicate that it's a custom
        // relationship.
        // The status of each payment is checked. If any
        // payment is past due, the invoice is marked past due
        // and if all payments are paid, the invoice is marked
        // as paid in full, otherwise the invoice is marked as current.
        for ( Scheduled_Payment__c payment : 
            invoice.Scheduled_Payments__r )
        {
           if ( payment.Status__c != newStatus )
           {
               if ( payment.Status__c == 'Pending'
                   && newStatus == 'Paid in Full' )
               {
                   newStatus = 'Current';
               }
               else if ( payment.Status__c == 'Past Due' )
               {
                   newStatus = 'Past Due';
               }
           }
        } // payments loop
  
        if ( invoice.Status__c != newStatus )
        {
            // The invoice status needs to be changed.
            // Change the invoice's status and add it to the list
            // of invoices to update. Salesforce.com will only modify
            // the actual field you update in code, it will not re-save
            // the values you pulled from the database.
            invoice.Status__c = newStatus;
            updateInvoices.add( invoice );
        }
  
        // If you're at the batch size limit, update Salesforce.com
        // and clear the update list.
        if ( updateInvoices.size() == MAX_BATCH_SIZE )
        {
            Database.update( updateInvoices );
            updateInvoices.clear();
        }
    } // queryResults loop.
  
    // Update Salesforce.com if there are any pending changes.
    if ( updateInvoices.size() > 0 )
    {
        Database.update( updateInvoices );
    }
}
This trigger fires after a payment is updated, and if the payment's status changes it checks to see if the parent invoice needs its status updated as well. For example if the payment is past due, the parent invoice changes its status to past due.  

Now that both triggers have been completed we will need to test them, which will do in our next post.  

Monday, 25 October 2010

Apex Triggers

So this weekend, following the Giants punching their trip to the World Series (Fear the Beard), I officially registered for Dreamforce 2010. I advise that everyone who will be attending this year to create their Chatter profile and sign-up for sessions using the Agenda Builder as soon as possible. Also any recommendations would be appreciated since it will be my first time attending any type of cloud computing event.

Ok so let’s jump into Apex triggers. If you recall from my previous post, we will use Apex Code to create these triggers and complete our scheduled payments. Apex Triggers operate like most standard database triggers, you define the trigger name for when the trigger fires and write code to act on the event. This can be before or after insert, update, or delete.

For our application we will be writing two triggers. The first trigger will create scheduled payment objects for an invoice when the invoice is created, and the second trigger will update the invoice's status when the status of a scheduled payment changes. Begin by opening the Scheduled Invoice object definition by clicking ‘Setup’, then ‘Build’, and finally ‘Objects’. At the bottom of the page you will find the ‘Triggers’ section, at which point you will select ‘New’. This will bring up the Apex Code editor, which you will see in the following screen shot:



For our first Trigger we need to replace the blank template by copying the following code and pasting it into the editor:


// After a new entry is insert into the Scheduled Invoice custom object table
// this trigger will fire and create all the scheduled payment objects for
// that invoice.
trigger ScheduledInvoice_After_Insert on Scheduled_Invoice__c
    ( after insert ) 
{
    // Apex Code's maximum batch size for updates, inserts
    // and deletes. The final key word indicates a constant.
    final Integer MAX_BATCH_SIZE = 200; 
    List<Scheduled_Payment__c> newPayments;
    Date endDate;
    Boolean isMonthly = false;
    Integer daysInPeriod = 0;
    Integer numberOfPeriods = 0;
    Double paymentPerPeriod = 0;
    Date dueDate;
    
    newPayments = new List<Scheduled_Payment__c>();
  
    // Loop through the new invoices and create scheduled payments
    // for each one depending on their settings.
    // The trigger object has two properties containing lists of updated
    // objects, new and old. For insert triggers, only the new property
    // has values.
    // The for loop below is similar to a foreach loop in C#. The C#
    // equivalent would be foreach ( Object var in varArray )
    for ( Scheduled_Invoice__c invoice : trigger.new )
    {
        // Convert the Duration in Months to an actual number of
        // periods. If the schedule type is monthly, then the number
        // of periods is equal to the duration in months, otherwise
        // you need to calculate it.
        if ( invoice.Schedule_Type__c == 'Monthly' )
        {
            numberOfPeriods = invoice.Duration_in_Months__c.intValue();
            isMonthly = true;
        }
        else
        {
            Integer numberOfDays;
  
            // For weekly and bi-weekly, calculate the end date first.
            endDate = invoice.Effective_Date__c.addMonths( 
                invoice.Duration_in_Months__c.intValue() );
            // Next, get the number of days.
            numberOfDays = invoice.Effective_Date__c.daysBetween(
                endDate );
            // Setup the number of days in the period for weekly and
            // bi-weekly payments.
            if ( invoice.Schedule_Type__c == 'Weekly' )
                daysInPeriod = 7;
            else
                daysInPeriod = 14;
            // Divide the number of days by the number of days in the
            // period to get the number of periods.
            numberOfPeriods = Math.floor( numberOfDays / 
                daysInPeriod ).intValue();
        }            
  
        // Calculate the payment per period.
        paymentPerPeriod = invoice.Total_Amount_Due__c / 
            numberOfPeriods;
        
        // Generate a new scheduled payment object for each
        // period of the invoice.
        for ( Integer x = 0; x < numberOfPeriods; x++ )
        {
            // Calcualte the due date for this period.
            if ( isMonthly )
            {
                dueDate = invoice.Effective_Date__c.addMonths( x + 1 );
            }
            else
            {
                dueDate = invoice.Effective_Date__c.addDays( 
                    daysInPeriod * ( x + 1 ) );
            }
            
            // Create the new scheduled payment object and add it
            // to the list of new payment objects to be inserted.
            // For SObjects (Salesforce Objects) you can specify the
            // property values in the constructor by using the syntax
            // <Property Name> = <value>.
            newPayments.add( new Scheduled_Payment__c(
                Invoice__c = invoice.Id, Status__c = 'Pending',
                Due_Date__c = dueDate,
                Amount_Due__c = paymentPerPeriod ) );
  
            // If you've hit the maximum batch size, insert the new
            // data and clear the list.
            if ( newPayments.size() == MAX_BATCH_SIZE )
            {
                Database.insert( newPayments );
                newPayments.clear();
            }
        }
    }
  
    // If you still have data you need to insert, insert it now.
    if ( newPayments.size() > 0 )
    {
        Database.insert( newPayments );
  }
}

Once again the final step is to simply click ‘Save’.  Normally this code would be placed in a completely separate Apex Code class editor, but in order to be concise we will include it directly into the trigger. We will go over Apex Code classes when we learn about VisualForce next week. It will make more sense in that context.

In the next post we will create the second Trigger and test both of them when they are in place.