4

I have a trigger that inserts a new child record (Training Session) below Training Module using a third object (Scheduler). Training Module contains an external ID called Training_Module_ID, but when I enter that into Scheduler and save it gives me the following error:

Error: Invalid Data. Review all error messages below to correct your data. Apex trigger createTrainingSession caused an unexpected exception, contact your administrator: createTrainingSession: execution of AfterInsert caused by: System.StringException: Invalid id: Test: Trigger.createTrainingSession: line 7, column 1

However, if I type in the 15 character Record ID, then the Training Session is created as intended. How can I make this work using the external ID?

trigger createTrainingSession on Scheduler__C(after insert) {
    List < Training_Session__c > TStoinsert = new List < Training_Session__c > ();
    for (Scheduler__C o: Trigger.new) {
        if (o.Type__c == 'Schedule Training') {
            Training_Session__c v = new Training_Session__c();
            v.Training_Module_ID__c = o.Training_Module_ID__c;

            TStoinsert.add(v);
        }

    }
    try {
        insert TStoinsert;
    } catch (system.Dmlexception e) {
        system.debug(e);
    }
}

1 Answer 1

1

It would be best to isolate the code that works with the external ID string and keep as much of your logic as possible working with true internal ID values. If that is not possible you should adopt a naming convention to be clear which field is which e.g. Training_Module_ID__c and Training_Module_ExtID__c.

If you can't make those changes, the trigger would need to look something like this where it does the translation from external ID to ID as part of its logic:

trigger createTrainingSession on Scheduler__c(after insert) {

    Set<String> extIds = new Set<String>();
    for (Scheduler__c s: Trigger.new) {
        if (s.Type__c == 'Schedule Training') {
            extIds.add(s.Training_Module_ID__c);
        }
    }

    if (extIds.size() > 0) {

        Map<String, Id> extIdToId = new Map<String, Id>();
        for (Training_Module__c tm : [
                select Id, Training_Module_ID__c
                from Training_Module__c
                where Training_Module_ID__c in :extIds
                ]) {
            extIdToId.put(tm.Training_Module_ID__c, tm.Id);
        }

        List <Training_Session__c> tss = new List <Training_Session__c>();
        for (Scheduler__c s: Trigger.new) {
            if (s.Type__c == 'Schedule Training') {
                tss.add(new Training_Session__c(
                        Training_Module_ID__c = extIdToId.get(s.Training_Module_ID__c)
                        ));
            }
        }
        insert tss;
    }
}
2
  • Thanks Keith! That worked like a charm. Just to clarify your initial comments. 1) Are you suggesting I use internal ID values because external IDs are only meant for data imports? My entire salesforce instance has the standard ID set to autonumber, and so we rely on the external IDs for searching. What other limitations might this pose? 2) Is Salesforce confusing the external ID Training_Module_ID with one of the standard ID fields?
    – Dominik
    Aug 6, 2014 at 13:57
  • 1
    @Dominik Every Salesforce record has an ID - a 15 character generated value - and a Name - manually set or auto-numbered. ID fields are automatically indexed and when referenced from another object (a foreign key field) provide good lookup UI widgets. These relationships can also be master-detail that support cascaded delete and rollups. By contrast external ID fields are just bits of text. By all means allow users to search on the external IDs if that is what you need but make relationships between object proper Salesforce IDs.
    – Keith C
    Aug 6, 2014 at 14:02

You must log in to answer this question.

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