+ Start a Discussion
patskepatske 

*bump* Cascading? Triggers Help

Hi Guys,

I have a problem that has me a little stumped. I have a few triggers in my sandbox some written by me some written by another developer. On their own they work fine but they are dependant on each other in that one creates records that the other triggers off of so when they are made active I start to get some funny results and anusual errors popping every now and then.

Here's just one of the errors I'm receiving.

(1)
paymentAA: execution of AfterUpdate

caused by: System.DmlException: Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE
_ENTITY, invoiceAction: execution of AfterInsert

caused by: System.ListException: Duplicate id in list: a0aT00000009RIzIAM

Class.InvoiceAction.updateInvo
iceAmount: line 54, column 9
Trigger.invoiceAction: line 4, column 9

Class.PaymentAA.CreateInvoice: line 65, column 12
Class.PaymentAA.CreatePayment: line 29, column 13
Trigger.paymentAA: line 2, column 4

And below are the lines of code referenced by this error well the main 'perpetraitors' as I see it

One important note to mention is that this code gives seems to only give me errors when I have more than 1 Invoice_Item__c in the list to insert. When there is just one it works fine but when there is 2 or more it seems as though it crashes I'm guessing because it calles the trigger on invoice_item__c twice too quickly? Before both records are committed? I'm not sure but any help would be great. Thanks in advance.

Code:
FROM PaymentAA

...
// define a new invoice 
           Emp_Invoice__c newinvoice = new Emp_Invoice__c();
           
           // update the value of the account it relates to to the same as the sales prospect
           newinvoice.Account__c = spAcc;
           
           // relate to sales prospect
           newinvoice.Sales_Prospect__c = spID;
           
           // sets the date on the invoice
           newinvoice.Invoice_Date__c = date.today();
                      
           // commits the new invoice to database
           insert newinvoice;

           // returns a list of all the order items associated with sales prospect
           List<Order_Item__c> orderitems = [select Name, Order_Line_Total__c from Order_Item__c where Sales_Prospect__c = :spID];
           List<Invoice_Item__c> ii = new List<Invoice_Item__c>();
           // iterate over all the line items in the order
           for (Order_Item__c oi : orderitems) {
               // create a new invoice item corresponding to each order item with the same values
               ii.add(new Invoice_Item__c(Invoice__c = newinvoice.Id, Order_Item__c = oi.Id, Line_Total_ex_GST__c = oi.Order_Line_Total__c));
           }
           insert ii;
...



AND THE WHOLE Class.InvoiceAction This Triggers from "Invoice_Item__c" after insert / after update 

public class InvoiceAction { public static void updateInvoiceAmount(Invoice_Item__c[] newInvItems) { //array of invoices that have a new amount paid List<Emp_Invoice__c> myInvoices = new List<Emp_Invoice__c>(); //iterate over all the new invoice items in this batch for (Invoice_Item__c thisInvItem:newInvItems ) { //ingnore invoice items with out an invoice if (thisInvItem.Invoice__c != null){ //get all invoice items for this invoice List<Invoice_Item__c> allInvItems = [SELECT Id, Line_Total_ex_GST__c, GST_Amount__c, Amount_Paid__c FROM Invoice_Item__c WHERE Invoice__c = :thisInvItem.Invoice__c limit 1000]; // get the invoice for this invoice item and lock it Emp_Invoice__c thisInvoice = [SELECT Id, Amount_ex_GST__c, GST_Amount__c, Amount_Paid__c FROM Emp_Invoice__c WHERE Id = :thisInvItem.Invoice__c limit 1 for update]; decimal sumApPaid = 0.0; decimal sumLtPaid = 0.0; decimal sumGstPaid = 0.0; // sum up all the invoice items for this invoice for (Invoice_Item__c cInvItem:allInvItems ) { if(cInvItem.Amount_Paid__c != null){ sumApPaid += cInvItem.Amount_Paid__c; } if(cInvItem.Line_Total_ex_GST__c != null){ sumLtPaid += cInvItem.Line_Total_ex_GST__c; } if(cInvItem.GST_Amount__c != null){ sumGstPaid += cInvItem.GST_Amount__c; } } // update the amuount paid for this invoice thisInvoice.Amount_Paid__c = sumApPaid; thisInvoice.Amount_ex_GST__c = sumLtPaid; thisInvoice.GST_Amount__c = sumGstPaid; // add this invoice to the array of invoices that have a new amount paid System.debug('[InvoiceAction -> executeTriggerAfter] adding amount paid=' + thisInvoice.Amount_Paid__c + ' to invoice id=' +thisInvoice.id); myInvoices.add(thisInvoice); } } //update all invoices that have a new amount paid update myInvoices; } }

 




Message Edited by patske on 06-19-2008 03:40 PM

Message Edited by patske on 06-19-2008 03:44 PM
mikeegarmikeegar

The error you are getting is saying that you are trying to call update with two entries for the same object. Try changing:

List<Invoice_Item__c> ii

to

Set <Invoice_Item__c> ii

This will insure that no more than one entry will appear for each object.
patskepatske
I'm pretty sure sets are only for primative data types and can't be used for Objects.

The error I think is coming from the trigger that collates 'myInvoices" and updates them.. Since sometimes I will send 2 or more invoice items linked to the same invoice for update and this
errors the trigger.

Anyone else have any ideas?

Basically on how I can get the trigger to work?

invoice_Item__c is a child of Emp_Invoice__c for all of your information
BoxBox


Hey,

Sets can contain SObjects and would get rid of the multiple ID issues.  If you want to track information between triggers than use a class with static members, accessors and gettors e.g.


Code:
public class CStaticTracker 
{
    public static set<Id> setId = new set<Id>();
    
    public static void AddId(Id thisId)
    {
        setId.add(thisId);
    }
    
    public static void RemoveId(Id thisId)
    {
        setId.remove(thisId);
    }
    
    public static boolean ContainsId(Id thisId)
    {
        return setId.contains(thisId);
    }
    
    public static void EmptySet()
    {
        setId.clear();
    }
}

 
This will let your track state across cascading triggers, these will all live for the duration of the running thread.  So in your triggers you can restrict processing of any ID that has been processed before but you can extend this to much more complex behaviour if required.
patskepatske
Hey guys,

Thanks for your help.
I tried this in my code Set <Invoice_Item__c> ii but no dice... Any reason why it wouldn't work for me?

The error I get is:

Error: Compile Error: Set of SOBJECT:Invoice_Item__c not allowed at line 4 column 6