You need to sign in to do that
Don't have an account?
Eric Smith 9
Problem trying to update SObject with master-detail field record details in Apex
I have some Apex code that returns data for a LWC datatable component. If I have a lookup field, I'm able to return the lookup record's Name field value along with the Id. If it is a Master-Detail field, I get an error: "salesforce relationship object__r is not editable".
I'm trying to implement a work-around mentioned here:
Generic sobject, field not editable error on master-detail field (https://developer.salesforce.com/forums/?id=906F000000091KxIAI)
When I look at the records in line 190:
they do not contain the update I'm trying to add in line 184:
Any ideas on what I'm missing here?
This is the complete Apex Class
I'm trying to implement a work-around mentioned here:
Generic sobject, field not editable error on master-detail field (https://developer.salesforce.com/forums/?id=906F000000091KxIAI)
When I look at the records in line 190:
System.debug('records: ' + JSON.serializePretty(records));
they do not contain the update I'm trying to add in line 184:
so = (sobject)json.deserialize(str, sobject.class);
Any ideas on what I'm missing here?
This is the complete Apex Class
/** * * Based on a component (ItemsToApprove) created by: Alex Edelstein (Salesforce) * Based on a component (FlatTable) created by: J. Pipkin (OpFocus, Inc) * * Description: getColumnData * Get field information from a list of field names in order to build * the column definitions for the datatable * * getRowData * Take a List of Records and a List of Lookup Field Names and * use the recordId values in the lookup fields get the values * of the Name fields in the corresponding records. Return the * records that now include both the Id and Name for each lookup. * * 04/01/20 - Eric Smith - Version 1.0 * 04/14/20 - Eric Smith - Version 1.1 Cleaned up some error handling * 04/28/20 - Eric Smith - Version 1.2 Handle lookup Objects without a Name field & handle Master/Detail * **/ public with sharing class SObjectController { //this is just a convenient way to return multiple unique pieces of data to the component public class ReturnResults { List<SObject> rowData; String dtableColumnFieldDescriptorString; List<String> lookupFieldList; List<String> percentFieldList; List<String> noEditFieldList; list<String> timeFieldList; String objectName; } @AuraEnabled public static string getReturnResults(List<SObject> records, String fieldNames){ ReturnResults curRR = new ReturnResults(); if (records.isEmpty()) { // throw new MyApexException ('The datatable record collection is empty'); List<String> emptyList = new List<String>(); curRR.dtableColumnFieldDescriptorString = '{"label":"Empty Table", "fieldName":"Id", "type":"text"}'; curRR.lookupFieldList = emptyList; curRR.percentFieldList = emptyList; curRR.noEditFieldList = emptyList; curRR.timeFieldList = emptyList; curRR.rowData = records; curRR.objectName = 'EmptyCollection'; } else { String objName = records[0].getSObjectType().getDescribe().getName(); curRR = getColumnData(curRR, fieldNames, objName); curRR.rowData = getRowData(records, curRR.lookupFieldList, curRR.percentFieldList); curRR.objectName = objName; } return JSON.serialize(curRR); } @AuraEnabled public static ReturnResults getColumnData(ReturnResults curRR, String fields, String objectName) { SObjectType sobjType = ((SObject)(Type.forName('Schema.'+objectName).newInstance())).getSObjectType(); DescribeSObjectResult objDescribe = sobjType.getDescribe(); String datatableColumnFieldDescriptor = ''; String fieldType = ''; List<Schema.DescribeFieldResult> curFieldDescribes = new List<Schema.DescribeFieldResult>(); List<String> lookupFields = new List<String>(); List<String> percentFields = new List<String>(); List<String> noEditFields = new List<String>(); List<String> timeFields = new List<String>(); for (String fieldName : fields.split(',')) { Map<String, Schema.SObjectField> fieldMap = objDescribe.fields.getMap(); Schema.SObjectField fieldItem = fieldMap.get(fieldName); if (fieldItem == null) throw new MyApexException('could not find the field: ' + fieldName + ' on the object ' + objectName); Schema.DescribeFieldResult dfr = fieldItem.getDescribe(); curFieldDescribes.add(dfr); datatableColumnFieldDescriptor = datatableColumnFieldDescriptor + ',{"label" : "' + dfr.getLabel() + '", "fieldName" : "' + fieldName + '", "type" : "' + convertType(dfr.getType().name()) + '", "scale" : "' + dfr.getScale() + '"}'; switch on dfr.getType().name() { when 'REFERENCE' { lookupFields.add(fieldName); } when 'PERCENT' { percentFields.add(fieldName); } when 'TEXTAREA' { if (!dfr.isSortable()) noEditFields.add(fieldName); // Long Text Area and Rich Text Area } when 'ENCRYPTEDSTRING', 'PICKLIST', 'MULTIPICKLIST' { noEditFields.add(fieldName); } when 'CURRENCY', 'DECIMAL', 'DOUBLE', 'INTEGER', 'LONG' { // *** create scale attrib in datatableColumnFieldDescriptor and pass the getScale() values in that way. *** } when 'TIME' { timeFields.add(fieldName); } when else { } } } System.debug('final fieldDescribe string is: ' + datatableColumnFieldDescriptor); curRR.dtableColumnFieldDescriptorString = datatableColumnFieldDescriptor.substring(1); // Remove leading , curRR.lookupFieldList = lookupFields; curRR.percentFieldList = percentFields; curRR.noEditFieldList = noEditFields; curRR.timeFieldList = timeFields; return curRR; } @AuraEnabled public static List<SObject> getRowData(List<SObject> records, List<String> lookupFields, List<String> percentFields) { // Update object to include values for the Name field referenced by Lookup fields String objName = records[0].getSObjectType().getDescribe().getName(); Map<String, Set<Id>> objIdMap = new Map<String, Set<Id>>(); List<String> fields = lookupFields; // Get names of the related objects for(SObject so : records) { for(String lf : fields) { if(so.get(lf) != null) { Id lrid = ((Id) so.get(lf)); String relObjName = lrid.getSobjectType().getDescribe().getName(); if(!objIdMap.containsKey(relObjName)) { objIdMap.put(relObjName, new Set<Id>()); } objIdMap.get(relObjName).add(lrid); } } } // Lookup the Name field in the related object Map<String, Map<Id, SObject>> dataMap = new Map<String, Map<Id, SObject>>(); for(String obj : objIdMap.keySet()) { Set<Id> ids = objIdMap.get(obj); String nameField = getNameUniqueField(obj); SObject[] recs = Database.query('Select Id, ' + nameField + ' from ' + obj + ' where Id in :ids'); System.Debug('Name Field: '+obj+' - '+nameField); Map<Id, SObject> somap = new Map<Id, SObject>(); for(SObject so : recs) { somap.put((Id) so.get('Id'), so); } dataMap.put(obj, somap); } // Add new field values to the records for(SObject so : records) { // Divide percent field values by 100 for(String pf : percentFields) { if(so.get(pf) != null) { so.put(pf, double.valueOf(so.get(pf))/100); } } // Add new lookup field values for(String lf : fields) { if(so.get(lf) != null) { Id lrid = ((Id) so.get(lf)); String relObjName = lrid.getSobjectType().getDescribe().getName(); Map<Id, SObject> recs = dataMap.get(relObjName); if (recs == null) continue; SObject cso = recs.get(lrid); if (cso == null) continue; String relName; if (lf.toLowerCase().endsWith('id')) { relName = lf.replaceAll('(?i)id$', ''); } else { relName = lf.replaceAll('(?i)__c$', '__r'); } try { so.putSObject(relName, cso); } catch(exception e) { // Workaround for "Field is not editable" error (Master/Detail) String str = json.serialize(so); str = str.replaceFirst('\\}','}, "'+relName+'":'+json.serialize(cso)); so = (sobject)json.deserialize(str, sobject.class); } } } } System.debug('records: ' + JSON.serializePretty(records)); return records; } public class MyApexException extends Exception { } //convert the apex type to the corresponding javascript type that datatable will understand private static String convertType (String apexType){ switch on apexType { when 'BOOLEAN' { return 'boolean'; } when 'CURRENCY' { return 'currency'; } when 'DATE' { return 'date'; } when 'DATETIME' { return 'datetime'; // Custom type for this component } when 'DECIMAL', 'DOUBLE', 'INTEGER', 'LONG' { return 'number'; } when 'EMAIL' { return 'email'; } when 'ID' { return 'id'; } when 'LOCATION' { return 'location'; } when 'PERCENT' { return 'percent'; } when 'PHONE' { return 'phone'; } when 'REFERENCE' { return 'lookup'; // Custom type for this component } when 'TIME' { return 'time'; // Custom type for this component } when 'URL' { return 'url'; } when else { // throw new MyApexException ('you\'ve specified the unsupported field type: ' + apexType ); return 'text'; } } } //Get the 'Name' field for the given SObjectType private static String getNameUniqueField(String objectName) { String strResult = null; SObjectType sobjType = ((SObject)(Type.forName('Schema.'+objectName).newInstance())).getSObjectType(); DescribeSObjectResult objDescribe = sobjType.getDescribe(); Map<String, Schema.SObjectField> fieldMap = objDescribe.fields.getMap(); for(String fieldName : fieldMap.keySet()) { SObjectField objField = fieldMap.get(fieldName); Schema.DescribeFieldResult dfr = objField.getDescribe(); if(dfr.isNameField()) { strResult = dfr.getName(); break; } if(strResult != null) { return strResult; } } for(String fieldName : fieldMap.keySet()) { SObjectField objField = fieldMap.get(fieldName); Schema.DescribeFieldResult dfr = objField.getDescribe(); if(dfr.isAutoNumber()) { strResult = dfr.getName(); break; } if(strResult != null) { return strResult; } } for(String fieldName : fieldMap.keySet()) { SObjectField objField = fieldMap.get(fieldName); Schema.DescribeFieldResult dfr = objField.getDescribe(); if(dfr.isUnique()) { strResult = dfr.getName(); break; } } return strResult; } }
cyberflixhd (https://cyberflixtv.info/)