function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
dgdeyo09dgdeyo09 

Need Apex Triggers to roll-up a formula field

I have objects A, B, and C. Object B is an association object for A and C, so it has a master-detail with both of them. Object B also has a formula field which takes values from C. I want to roll up that formula field to A. I know I need to use Apex and Triggers to do this, but I'm just learning and need some help with how to set up the triggers and classes. Thanks to all who respond

Jake GmerekJake Gmerek

Hi you will need an after update trigger on Object B in order for this to work.

 

Something like this:

 

Trigger myTrigger on B (after insert, after update)
{
list<id> aIds = new list<id>();
for (B myBRecord: trigger.new){
  aIds.add(myBRecord.id);
}

list<B> bRecordsToTotal = new list<B>([select id, myField from b were aId in: aIds]);

map<string, list<B>> bMap = new map<string, list<B>>();

for (B myBRecord: bRecordsToTotal)
{
  if (!bMap.containsKey(myBRecord.aId))
    {
      bMap.put(myBRecord.aId, new List<B>());
    }
   bMap.get(myBRecord.aId).add(myBRecord)
}

List<A> aRecordsToUpdate = new List<A>([select id, fieldtoUpdate from A where id in: bMap.keyset() );

for (A myARecord: aRecordsToUpdate)
{
  decimal tempTotal = 0;
  for (B bRecord: bMap.get(myARecord.id))
  {
    tempTotal = tempTotal + bRecord.myField;
  }
  myARecord.fieldtoUpdate = tempTotal;
}
if (aRecordsToUpdate.size>0)
{
  update aRecordsToUpdate;
}
}

 So I wrote this real quick to give you an idea of what needs to happen.  I did not test it or try it out.  One problem that you will run into is that when the formula changes on the B records that will not kick off your trigger and update the A records, so your A record may be out of date until someone update one of the B records.  You may be able to do a second trigger on the C object to take care of that, but that would be the second step.  Let me know if you have any questions.

dgdeyo09dgdeyo09

Thanks for the response. I think I understand the overall logic that you put together here. Seeing as I am just learning Apex though, I'm a bit confused as to what is happening in your queries. I havn't come accross anything using the "aId in : aIds " syntax as of yet.

 

Also, something separate : What does mybRecord.aId actually refer to? 

Jake GmerekJake Gmerek

Yeah some of this is confusing because of a,b,c instead of actual object names.

 

"where aId in: aIds" is a way to capture all of the B records that relate to any A records that relate to any B records in the trigger.  Basically it is saying give me all the B records where the AId is contained in the set AIds that we created earlier.  We do this because we can not be sure that all the B records for a given A record are being sent to the query.  So in the first for loop, we gather the id of all the a records that relate to a B record in the query.  Then using that set of ids, we gather all the B records that relate to the A records so that we can do a real total and not just a total of the ones that have been created or changed and thus get passed to the query.

 

mybRecord.aid represents the lookup or Master-Detail field on the B record that relates back to an A record.  We are using it as the Key for that map so that we can associate one A record, through its id, will all the B records associated with that A record.

 

Hope this helps, let me know if you have any more questions.

dgdeyo09dgdeyo09

Ok that makes sense. I see how all the records are relating now.

 

Here is what I've come up with. The objects that I'm working with are a Line Item object which is being rolled up to a Invoice. But nothing is happening when I create a new Line Item in the invoice. I'm thinking I might be doing something wrong in relation to getting the Ids of the records...?

 

trigger LineItemTrigger on Line_Item__c (after insert, after update){

list<id> InvoiceIds = new list<id>();
for (Line_Item__c line: trigger.new){
  InvoiceIds.add(line.id);
}

list<Line_Item__c> LineItemsToTotal = new list<Line_Items__c>([select id, Cost__c from Line_Item__c where id in: InvoiceIds]);

map<string, list<Line_Item__c>> LineItemMap = new map<string, list<Line_Item__c>>();

for (Line_Item__c line: LineItemsToTotal)
{
  if (!LineItemMap.containsKey(line.id))
    {
      LineItemMap.put(line.id, new List<Line_Item__c>());
    }
   LineItemMap.get(line.id).add(line);
}

List<Invoice__c>  Invoices = new List<Invoice__c>([select id, Total_Cost__c from Invoice__c where id in: LineItemMap.keyset()]);

for (Invoice__c I: Invoices)
{
  decimal tempTotal = 0;
  for (Line_Item__c line: LineItemMap.get(I.id))
  {
    tempTotal = tempTotal + line.Cost__c;
  }
  L.Total_Cost__c = tempTotal;
}

if (Invoices.size() > 0)
{
  update Invoices;
}
}

 

 

Jake GmerekJake Gmerek

This line is wrong for sure:

 

 

InvoiceIds.add(line.id);

 

You are adding the line item ids to the list when you need the invoice id.  There should be a field on your line item that relates it to the invoice, either a lookup or master detail.  This field stores the id that you need.