3

I have a custom object record with a start date, an end date, and a amount field. I want to be able to take the difference between the two dates in number of months, divide the amount by the difference and then create related records based on the difference count. To make a basic schedule.

I think I have to do two for loops in my class, however, I've never done two at the same time before so I'm unsure how to get started.

I know that to get the total number of months I can do this:

Integer numOfMonths = start_date.monthsbetween(end_date) 

I know that to create the records I can do something like this:

for(Integer i = 0; i < numOfMonths; i++) {
    insert new Revenue_Schedules__c(
        Amount__c = amount/numOfMonths,
        Date__c = start_date.addmonths(numOfMonths), 
        );      

How do you loop through values in the newMap to get each variable and then loop again to get records to insert?

-=EDIT=-

input example: Revenue record with start_date = 10-1-2016, end_date = 9-1-2017, amount = 1100

output expectation: 12 Revenue Schedule records
* record 1, date = 10-1-2016 amount = 100
* record 2, date = 11-1-2016 amount = 100
* record 3, date = 12-1-2016 amount = 100
ect..

3
  • 3
    Never perform dml in a loop of any kind. There are very rare exceptions, but this is not one of them.
    – Adrian Larson
    Commented Nov 4, 2016 at 15:48
  • 1
    So you would add each iteration to a list and then outside the loop insert. I think I need help getting to the list part. Commented Nov 4, 2016 at 15:51
  • I think you need to elaborate on your expected inputs/outputs.
    – Adrian Larson
    Commented Nov 4, 2016 at 15:52

2 Answers 2

7

You should never do DML statements (such as insert) inside loops, this will easily get you off limits. Create a List outside the loop, iterate over your trigger in the first loop, create new records and add them to the list in the second loop, then insert everything outside the loop. Something like this:

List<Revenue_Schedule__c> new_records = new List<Revenue_Schedule__c>();

for (Your_Trigger_sObject__c current : trigger.new) {
    Integer numOfMonths = current.start_date.monthsbetween(current.end_date);
    for (Integer i=0; i < numOfMonths; i++) {
        new_records.add(
            new Revenue_Schedules__c(
                Amount__c = current.amount / numOfMonths,
                Date__c = current.start_date.addmonths(numOfMonths), 
            )
        );
    }
}

insert new_records;

The important is to keep the DML operation outside the loop. The same applies to queries, keep them always out.

3
  • 1
    +1. I'd also add that you can do a maximum of 150 DML operations during a single transaction, hence why as Mauricio Oliveira correctly says you should upload a collection of sObjects.
    – Dan Jones
    Commented Nov 4, 2016 at 16:02
  • So I kept thinking that because i could have multiple starting records I would have to loop through those to get the numofmonths and then loop again to get the new_records to insert. But you're saying you can just do it all in one go? Commented Nov 4, 2016 at 16:10
  • @DanWooding Yes. Just as you can do insert new Revenue_Schedules__c(... you can also add that Revenue_Schedules__c to a List<Revenue_Schedules__c> and insert the List<Revenue_Schedules__c>.
    – Dan Jones
    Commented Nov 4, 2016 at 16:23
2

This is what I would do if I were you. Make sure to use one trigger per object and use helper methods for your operations/DML.

This is assuming you need to relate your revenue records to the source record.

trigger customObjTrigger on Custom_Obj__c (after insert){

    if(trigger.isInsert){
        if(trigger.isAfter){
            cust_obj.createRevs(trigger.new);
        }
    }
}

Class with Method

public class custom_obj methods{

        public static void createRevs(List<Custom_Obj__c> newList){

            List<RevenueSchedule__c> revs = new List<Revenue_Schedule__c>();

            for(custom_obj__c c : newList){
                Integer months = c.StartDate__c.MonthsBetween(c.EndDate__c);
                for(Integer i = 0; i<months;i++){
                    RevenueSchedule__c rev = new RevenueSchedule__c();
                    rev.Custom_Obj__c = c.id;
                    rev.Amount__c = c.Amount__c/months;
                    rev.StartDate__c = c.StartDate__c.addMonths(i);
                    revs.add(rev);
                }
            }

            if(revs.size() > 0){
                insert revs;
            }

        }

    }
2
  • While this answer is technically as correct as the other answer, the code is needlessly complex and consumes far more CPU time than the other answer (close to 400% more CPU time, by my estimate). It won't exceed any governor limits, but it'll make bulk data loads a bit more painful to watch.
    – sfdcfox
    Commented Nov 4, 2016 at 16:28
  • Yeah, you could do away with the first for-loop to build the map and move the integer variable as is seen in the first answer. Good catch sfdcfox (of course), was just putting this together on the fly. Commented Nov 4, 2016 at 16:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .