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.
My way in this situations - using Service Locator
public class MyServiceLocator {
private static final Map<Type, Type> customTypesMap = new Map<Type, Type> {
MyIDatabase.class => MyDatabase.class,
// Services
MyICustomObjectService.class => MyCustomObjectService.class,
MyIHttpService.class => MyHttpService.class,
// DAOs
MyICustomObjectDao.class => MyCustomObjectDao.class
};
private static final Map<Type, Type> testTypesMap = new Map<Type, Type> {
// Mocks
MyICustomObjectDao.class => MyTestCustomObjectDao.class,
MyIHttpService.class = MyTestHttpService.class
};
public static Type resolve(Type t) {
if (Test.isRunningTest()) {
if (testTypesMap.containsKey(t)) {
return testTypesMap.get(t);
}
}
if (customTypesMap.containsKey(t)) {
return customTypesMap.get(t);
}
return t;
}
}
And in MyTestCustomObjectDao I can throw exceptions.
For different behaviors you can add flag for unit test (throw or not throw exceptions).
Best Answer
To roll back a partially failed transaction, you can use the following code: