7

I am working on creating records for a custom object type in Salesforce. There is also a line item object which has a master-detail relationship.

  • Invoice = Master
  • Invoice Line Item = Detail

I am using JAVA to create these records after parsing an input XML file. The logic I am using to create invoice is as follows

  • Parse XML file, create Invoice object for each Invoice node.
  • Set the relevant fields.
  • Store in SObject array.
  • After all invoices have been created, use one UPSERT call to create records.

    UpsertResult[] upsertResults = connection.upsert("Invoice_ID__c", invoiceArray);

This is working fine. Now I need to create the invoice details. They are also in the XML file as child nodes of the Invoice node. I am a little confused about how to proceed. Since each Invoice detail line item will need to store the ID of its parent invoice which needs to be created first.

Is there a way of creating the master-detail records at the same time efficiently? If so where can I store the detail records at the time of calling the UPSERT call on the array storing all my Invoice records?

Thanks.

3 Answers 3

11

You're doing it right, you will need to create the Master records first before attempting to create the child records.

You have a couple of options :

Using the External Id : You can use the Invoice_Id__c to hook up to the parent. Here's an example , where you instantiate an instance of the parent, setting the External Id on it, and then just set that reference on the child record. You can then upsert the child records.

An important consideration is "When batch updating multiple records where the external ID is the same for two or more records in your batch call, those records will be marked as errors in the UpsertResult file. The records will be neither created or updated."

Using the Salesforce Id : If the upsert of the parent records has been successful, you will be able to get the Id from each element in the Upsert Result, using the getId() method.

I'm guessing Invoice_Id__c is the external id that you're using to upsert and will be available on both the parent and child records, to establish the relationship.

You could add all the child records to a Map <Invoice_Id__c, List<InvoiceLineItem>>

After successful upsert of parent, iterate through the UpsertResult, grabbing the relevant parent node and set the resulting id on all the child InvoiceLineItems in that list corresponding to that parent Id.

3
  • Since I went down this route, I will use the external id option. Thanks. Also, what are the best practices around handling the limit imposed by the API, i.e 200 records created/upserted at a time.
    – Richard N
    Commented Dec 11, 2012 at 15:42
  • Added an important consideration around duplication of external ids in a batch upsert. Whilst you would like to leverage batching and use 200, it often needs to be reduced depending on the frequency of occurrence of the external Id. Commented Dec 11, 2012 at 18:33
  • Can you please explain about this further? I have multiple invoice-detail records which refer to the same external ID(parent Invoice) and the upsert is working fine.
    – Richard N
    Commented Dec 12, 2012 at 16:37
10

What I would do in your situation is create a custom webservice method that would allow me to insert the data in one API request. What you could do is create a wrapper class that contains the Invoice and and array of the Invoice Line Items that you pass into this method. Then, in the implementation of the webservice method, you could handle first inserting the Invoice, then associating the line items to that invoice using the relationship fields and then inserting those.

Pseudo-ish Example:

global class MyInvoiceClass {

  webservice static Boolean insertInvoices(InvoiceWrapper[] invoices) {
    // insert invoices
    // insert line items 
    return true; // or false if there is a problem
  }

  global class InvoiceWrapper {
    webservice Invoice__c invoice;
    webservice Invoice_Line__c[] invoiceLines;
  }

}

This methodology allows you to reduce the api requests to 1 call and handle all of the implementation on the server side.

2
  • Thanks for sharing about the WebService method. Upvoted.
    – Richard N
    Commented Dec 11, 2012 at 15:41
  • 1
    +1. I've answered something similar on SO: stackoverflow.com/q/13271557/313628. Webservice means less trips over the network, faster results, transactions taken care of (would be stupid to leave headers in if invoice line items failed on new validation rule Java is unaware of). Whenever in normal DB you'd think "stored procedure" - there's high chance in SF it should be Apex exposed as webservice.
    – eyescream
    Commented Dec 11, 2012 at 19:49
0

Well i am not that aware of Java technology, but if you do it via salesforce then you can try doing this by creating a map of invoice and invoice line item object using the Invoice_Id_c field which will be available on both parent-child objects. You can add all the child records in this map. You can also try https://appexchange.salesforce.com/listingDetail?lis tingId=a0N3000000B53dYEAR if this helps you out here as this app has invoice and invoice line item objects in a Master Detail Relationship. Upsert can easily be done automatically as it is done via integration with Chargebee.

You must log in to answer this question.

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