Invokable Class for Flow
This is an example class, which can be invoked by a standard Flow. This is quite useful as it shows how you can use Salesforce standard automation practices to drive NetSuite record creation. Using this format you can extend to Update or Read in a similar fashion.
This basic example shows how easy it is to create robust automation in Salesforce to facilitate record creation in NetSuite. Extending upon this, using the Global API requests in this help site you can perform many automated retrieve, create, or update functions directly rather than requiring extensive middleware or endpoint processing to perform.
Apex Class: BW_CreateNetSuiteCompany_Invokable Utilizing @InvocableMethod allows this class to be selected in Flow, as an Apex Action.
public class BW_CreateNetSuiteCompany_Invokable {
@InvocableMethod(label='Create NetSuite Company' description='Creates NetSuite Companies invoked by Flow' category='Account')
// The main method to be implemented. The Flow calls this at run time.
public static List <Results> execute (List<Requests> requestList) {
// Get the user input from the flow
String testMode = requestList[0].testMode;
String firstName = requestList[0].firstName;
String lastName = requestList[0].lastName;
String companyName = requestList[0].companyName;
String accountNumber = requestList[0].AccountNumber; //accountNumber
String phone = requestList[0].Phone; //phone
String website = requestList[0].Website; //url
String billingStreet = requestList[0].BillingStreet;
String billingCity = requestList[0].BillingCity;
String billingState = requestList[0].BillingState;
String billingPostalCode = requestList[0].BillingPostalCode;
String shippingStreet = requestList[0].ShippingStreet;
String shippingCity = requestList[0].ShippingCity;
String shippingState = requestList[0].ShippingState;
String shippingPostalCode = requestList[0].ShippingPostalCode;
String subsidiaryId = requestList[0].SubsidiaryID;
//String taxNumber = requestList[0].taxNumber; //Example of custom Field SF: ncf_entity_tax_reg_no__c NS: custentity_tax_reg_no
//Create a Results object to hold the return values
Results response = new Results();
if (testMode == 'sendtonetsuite') { //using the testMode parameter filled with 'sendtonetsuite' is required for this class, as a security feature. You may want to remove this for production deploy.
try{
List<Object> netSuiteCustomerList = new List<Object>();
Map<String, Object> netSuiteCustomerMap = new Map<String, Object>();
netSuiteCustomerMap.put('subsidiary', new Map<String, Object>{'internalId'=>'8'}); //subsidiaryId);
//Assign value of Account to NetSuite Customer
netSuiteCustomerMap.put('accountNumber',accountNumber);
netSuiteCustomerMap.put('companyName',companyName);
netSuiteCustomerMap.put('phone',phone);
netSuiteCustomerMap.put('url',website);
//netSuiteCustomerMap.put('custentity_tax_reg_no',taxNumber); //Custom Field example
String billingAddr = String.isNotBlank(billingStreet) ? billingStreet : '';
String billingAddr1 = '';
String billingAddr2 = '';
String billingAddr3 = '';
if(billingAddr.length() >= 0 && billingAddr.length() <= 150){
billingAddr1 = billingAddr.substring(0,billingAddr.length());
}else if(billingAddr.length() > 150 && billingAddr.length() <= 300){
billingAddr1 = billingAddr.substring(0,150);
billingAddr2 = billingAddr.substring(150,billingAddr.length());
}else if(billingAddr.length() > 300 && billingAddr.length() <= 450){
billingAddr1 = billingAddr.substring(0,150);
billingAddr2 = billingAddr.substring(150,300);
billingAddr3 = billingAddr.substring(300,billingAddr.length());
}else if(billingAddr.length() > 450){
billingAddr1 = billingAddr.substring(0,150);
billingAddr2 = billingAddr.substring(150,300);
billingAddr3 = billingAddr.substring(300,450);
}
Map<String, Object> billingAddrFieldMap = new Map<String, Object>();
billingAddrFieldMap.put('addr1' , billingAddr1);
billingAddrFieldMap.put('addr2' , billingAddr2);
billingAddrFieldMap.put('addr3' , billingAddr3);
billingAddrFieldMap.put('city' , billingCity);
billingAddrFieldMap.put('state' , billingState);
billingAddrFieldMap.put('zip' , billingPostalCode);
Map<String, Object> abaAddressForBillingMap = new Map<String, Object>();
abaAddressForBillingMap.put('addressbookAddress' , billingAddrFieldMap);
String shippingAddr = String.isNotBlank(shippingStreet) ? shippingStreet : '';
String shippingAddr1 = '';
String shippingAddr2 = '';
String shippingAddr3 = '';
if(shippingAddr.length() >= 0 && shippingAddr.length() <= 150){
shippingAddr1 = shippingAddr.substring(0,shippingAddr.length());
}else if(shippingAddr.length() > 150 && shippingAddr.length() <= 300){
shippingAddr1 = shippingAddr.substring(0,150);
shippingAddr2 = shippingAddr.substring(150,shippingAddr.length());
}else if(shippingAddr.length() > 300 && shippingAddr.length() <= 450){
shippingAddr1 = shippingAddr.substring(0,150);
shippingAddr2 = shippingAddr.substring(150,300);
shippingAddr3 = shippingAddr.substring(300,shippingAddr.length());
}else if(shippingAddr.length() > 450){
shippingAddr1 = shippingAddr.substring(0,150);
shippingAddr2 = shippingAddr.substring(150,300);
shippingAddr3 = shippingAddr.substring(300,450);
}
Map<String, Object> shippingAddrFieldMap = new Map<String, Object>();
shippingAddrFieldMap.put('addr1' , shippingAddr1);
shippingAddrFieldMap.put('addr2' , shippingAddr2);
shippingAddrFieldMap.put('addr3' , shippingAddr3);
shippingAddrFieldMap.put('city' , shippingCity);
shippingAddrFieldMap.put('state' , shippingState);
shippingAddrFieldMap.put('zip' , shippingPostalCode);
Map<String, Object> abaAddressForShippingMap = new Map<String, Object>();
abaAddressForShippingMap.put('addressbookAddress' , shippingAddrFieldMap);
Map<String, Object> abBillingShippingMap = new Map<String, Object>();
abBillingShippingMap.put('addressbook' , new List<Object>{abaAddressForBillingMap,abaAddressForShippingMap});
netSuiteCustomerMap.put('addressbookList' , abBillingShippingMap);
//Enable This Section for Custom Fields
/*
// Initializing the Custom Fields
List<Object> custCustomFieldList = new List<Object>();
Map<String, Object> custCustomField1 = new Map<String, Object>();
custCustomField1.put('fieldType', 'string');
custCustomField1.put('scriptId', 'custentity_tax_reg_no');
custCustomField1.put('value', taxNumber);
custCustomFieldList.add(custCustomField1);
// Adding the Customer Custom Fields
netSuiteCustomerMap.put('customFieldList', new Map<String, Object>{'customField'=>custCustomFieldList});
*/
netSuiteCustomerList.add(netSuiteCustomerMap);
Map<String, Object> reqJSONMap = new Map<String, Object>();
reqJSONMap.put('customers' , netSuiteCustomerList);
String reqJSON = JSON.serialize(reqJSONMap);
Map<String, Object> finalReqMap = new Map<String, Object>();
finalReqMap.put('version' , '1.0');
finalReqMap.put('action' , 'createCustomer');
finalReqMap.put('RequestJSON' , reqJSON);
System.debug('Request ::: ' + finalReqMap);
//Namespace should be same as breadwinner objects in this org
//Map<String, Object> respMap = ns_rc_02.BreadwinnerNetSuiteAPI.call(finalReqMap);
Map<String, Object> respMap = breadwinner_ns.BreadwinnerNetSuiteAPI.call(finalReqMap);
System.debug('Response ::: ' + respMap);
response.companyInternalId = 'Blank';
//Take the NetSuite response map and convert to single string
String nsResponseString = '';
for(String key : respMap.keySet()){
if(nsResponseString.length() !=0){
nsResponseString += '::';
}
nsResponseString += key + '=' + respMap.get(key);
}
response.netSuiteResponse = nsResponseString;
}
catch(Exception e) {
System.debug('Exception Error ::: ' + e);
response.netSuiteResponse = 'Exception: ' + e;
}
} //Only executes if test mode set correctly. This should be removed/reworked prior to production deploy.
// Send return values to Flow
//Add the return values to the Results object
//response.outputMember = outputMember;
response.testMode = testMode;
response.firstName = 'name';
response.lastName = 'changes';
response.companyName = companyName;
//Wrap the Results object in a List container
//(an extra step added to allow this interface to also support bulkification)
List<Results> responseWrapper= new List<Results>();
responseWrapper.add(response);
return responseWrapper;
}
public class Requests {
//@InvocableVariable(label='Records for Input' description='Creates NetSuite Companies invoked by Flow' required=false)
//public List<SObject> inputCollection;
@InvocableVariable(label='testMode')
public string testMode;
@InvocableVariable(label='firstName')
public string firstName;
@InvocableVariable(label='lastName')
public string lastName;
@InvocableVariable(label='companyName')
public string companyName;
@InvocableVariable(label='AccountNumber')
public string AccountNumber;
@InvocableVariable(label='Phone')
public string Phone;
@InvocableVariable(label='Website')
public string Website;
@InvocableVariable(label='BillingStreet')
public string BillingStreet;
@InvocableVariable(label='BillingCity')
public string BillingCity;
@InvocableVariable(label='BillingState')
public string BillingState;
@InvocableVariable(label='BillingPostalCode')
public string BillingPostalCode;
@InvocableVariable(label='ShippingStreet')
public string ShippingStreet;
@InvocableVariable(label='ShippingCity')
public string ShippingCity;
@InvocableVariable(label='ShippingState')
public string ShippingState;
@InvocableVariable(label='ShippingPostalCode')
public string ShippingPostalCode;
@InvocableVariable(label='SubsidiaryId')
public string SubsidiaryId;
//Custom Field Example
/*
@InvocableVariable(label='taxNumber')
public string taxNumber;
*/
}
public class Results {
//@InvocableVariable(label='Records for Output' description='Creates NetSuite Companies invoked by Flow' required=false)
//public SObject outputMember;
@InvocableVariable(label='testMode')
public string testMode;
@InvocableVariable(label='firstName')
public string firstName;
@InvocableVariable(label='lastName')
public string lastName;
@InvocableVariable(label='companyName')
public string companyName;
@InvocableVariable(label='companyInternalId')
public string companyInternalId;
@InvocableVariable(label='netSuiteResponse')
public string netSuiteResponse;
}
}
//Original from: https://mukulmahawariya11.medium.com/calling-invocable-apex-from-flow-838879a332f7
//Retooled for standard @invokable according to SF Docs: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_annotation_InvocableMethod.htm
In a Salesforce Flow you will add an Apex Action, passing the desired values in and retrieving the NetSuite Response back:


Note how recordId is used to ensure the Flow is operating on the Account it was clicked from.






Adding an Action to the Flow that calls the Apex Class is what is required.


Here you will set all the input and output fields needed. *Note: The fields in the class Requests and Results section(s) are indicated with @InvocableVariable. The class provides which fields are selectable in the above Flow's detail page, so if it's not able to be selected you will need to update the class.

Finally, the responses that NetSuite sends back will indicate success, or failure in creating the NetSuite Company from this Salesforce Account.
