google-site-verification: google61fe8ba583a51912.html
Building NetSuite SuiteTalk Integrations in Java

NetSuite's SuiteTalk web services provide a robust SOAP-based API for integrating external applications with NetSuite ERP. This comprehensive guide covers implementing SuiteTalk in Java, from basic setup to advanced operations.

Architecture Overview

Java Application → SuiteTalk SOAP Client → NetSuite Web Services → WSDL-based Stubs → SOAP API Endpoints → Token-based Authentication → RESTlets (Alternative)

Prerequisites and Setup

Maven Dependencies

<properties> <suiteTalk.version>2023.2</suiteTalk.version> </properties> <dependencies> <!-- NetSuite SuiteTalk Web Services --> <dependency> <groupId>com.netsuite</groupId> <artifactId>netsuite-web-services</artifactId> <version>${suiteTalk.version}</version> </dependency> <!-- Apache CXF for SOAP Web Services --> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> <version>3.5.5</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http</artifactId> <version>3.5.5</version> </dependency> <!-- SOAP Dependencies --> <dependency> <groupId>javax.xml.ws</groupId> <artifactId>jaxws-api</artifactId> <version>2.3.1</version> </dependency> </dependencies>

Configuration Properties

# NetSuite Configuration netsuite.account=YOUR_ACCOUNT_ID [email protected] netsuite.password=your-password netsuite.role=3 netsuite.application-id=your-custom-app-id # Environment URLs netsuite.wsdl.url=https://webservices.netsuite.com/wsdl/v2023_2_0/netsuite.wsdl netsuite.endpoint.url=https://123456-sb1.suitetalk.api.netsuite.com/services/NetSuitePort_2023_2 # Connection Settings netsuite.connection.timeout=30000 netsuite.request.timeout=60000 netsuite.retry.max-attempts=3

Core SuiteTalk Implementation

1. NetSuite Service Factory

package com.yourapp.netsuite; import com.netsuite.webservices.platform.*; import com.netsuite.webservices.platform.core.*; import com.netsuite.webservices.platform.messages.*; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.xml.ws.BindingProvider; import java.util.Map; @Component public class NetSuiteServiceFactory { @Value("${netsuite.account}") private String account; @Value("${netsuite.email}") private String email; @Value("${netsuite.password}") private String password; @Value("${netsuite.role}") private String role; @Value("${netsuite.application-id}") private String applicationId; @Value("${netsuite.endpoint.url}") private String endpointUrl; /** * Create authenticated NetSuite service */ public NetSuiteService createNetSuiteService() { try { NetSuiteService service = new NetSuiteService(); NetSuitePortType port = service.getNetSuitePort(); // Configure endpoint BindingProvider bindingProvider = (BindingProvider) port; Map<String, Object> requestContext = bindingProvider.getRequestContext(); requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointUrl); // Set timeouts requestContext.put("com.sun.xml.internal.ws.request.timeout", 30000); requestContext.put("com.sun.xml.internal.ws.connect.timeout", 10000); return service; } catch (Exception e) { throw new RuntimeException("Failed to create NetSuite service", e); } } /** * Create passport for authentication */ public Passport createPassport() { Passport passport = new Passport(); // Set account passport.setAccount(account); // Set email and password RecordRef roleRef = new RecordRef(); roleRef.setInternalId(role); passport.setEmail(email); passport.setPassword(password); passport.setRole(roleRef); return passport; } /** * Create application info */ public ApplicationInfo createApplicationInfo() { ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.setApplicationId(applicationId); return applicationInfo; } }

2. Core NetSuite Connector

package com.yourapp.netsuite; import com.netsuite.webservices.platform.*; import com.netsuite.webservices.platform.core.*; import com.netsuite.webservices.platform.messages.*; import com.netsuite.webservices.setup.customization.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.util.*; @Service public class NetSuiteConnector { private static final Logger logger = LoggerFactory.getLogger(NetSuiteConnector.class); private final NetSuiteServiceFactory serviceFactory; public NetSuiteConnector(NetSuiteServiceFactory serviceFactory) { this.serviceFactory = serviceFactory; } /** * Get record by internal ID */ public ReadResponse getRecordById(String recordType, String internalId) { try { NetSuiteService service = serviceFactory.createNetSuiteService(); NetSuitePortType port = service.getNetSuitePort(); // Create passport and application info Passport passport = serviceFactory.createPassport(); ApplicationInfo applicationInfo = serviceFactory.createApplicationInfo(); // Create read request ReadRequest readRequest = new ReadRequest(); readRequest.setBaseRef(new RecordRef()); readRequest.getBaseRef().setInternalId(internalId); readRequest.getBaseRef().setType(RecordType.valueOf(recordType)); // Set application info header setApplicationInfo(port, applicationInfo); // Execute request return port.read(readRequest, passport); } catch (Exception e) { logger.error("Failed to get record by ID: {} - {}", internalId, e.getMessage()); throw new RuntimeException("NetSuite read operation failed", e); } } /** * Search records with criteria */ public SearchResult searchRecords(SearchRecord searchRecord, int pageSize) { try { NetSuiteService service = serviceFactory.createNetSuiteService(); NetSuitePortType port = service.getNetSuitePort(); Passport passport = serviceFactory.createPassport(); ApplicationInfo applicationInfo = serviceFactory.createApplicationInfo(); // Create search request SearchRequest searchRequest = new SearchRequest(); searchRequest.setSearchRecord(searchRecord); searchRequest.setPageSize(pageSize); setApplicationInfo(port, applicationInfo); SearchResponse searchResponse = port.search(searchRequest, passport); return searchResponse.getSearchResult(); } catch (Exception e) { logger.error("Search operation failed: {}", e.getMessage()); throw new RuntimeException("NetSuite search operation failed", e); } } /** * Add a new record */ public WriteResponse addRecord(Record record) { try { NetSuiteService service = serviceFactory.createNetSuiteService(); NetSuitePortType port = service.getNetSuitePort(); Passport passport = serviceFactory.createPassport(); ApplicationInfo applicationInfo = serviceFactory.createApplicationInfo(); // Create add request AddRequest addRequest = new AddRequest(); addRequest.setRecord(record); setApplicationInfo(port, applicationInfo); return port.add(addRequest, passport); } catch (Exception e) { logger.error("Add record failed: {}", e.getMessage()); throw new RuntimeException("NetSuite add operation failed", e); } } /** * Update existing record */ public WriteResponse updateRecord(Record record) { try { NetSuiteService service = serviceFactory.createNetSuiteService(); NetSuitePortType port = service.getNetSuitePort(); Passport passport = serviceFactory.createPassport(); ApplicationInfo applicationInfo = serviceFactory.createApplicationInfo(); // Create update request UpdateRequest updateRequest = new UpdateRequest(); updateRequest.setRecord(record); setApplicationInfo(port, applicationInfo); return port.update(updateRequest, passport); } catch (Exception e) { logger.error("Update record failed: {}", e.getMessage()); throw new RuntimeException("NetSuite update operation failed", e); } } /** * Delete record by internal ID */ public WriteResponse deleteRecord(String recordType, String internalId) { try { NetSuiteService service = serviceFactory.createNetSuiteService(); NetSuitePortType port = service.getNetSuitePort(); Passport passport = serviceFactory.createPassport(); ApplicationInfo applicationInfo = serviceFactory.createApplicationInfo(); // Create base reference RecordRef baseRef = new RecordRef(); baseRef.setInternalId(internalId); baseRef.setType(RecordType.valueOf(recordType)); // Create delete request DeleteRequest deleteRequest = new DeleteRequest(); deleteRequest.setBaseRef(baseRef); setApplicationInfo(port, applicationInfo); return port.delete(deleteRequest, passport); } catch (Exception e) { logger.error("Delete record failed: {} - {}", internalId, e.getMessage()); throw new RuntimeException("NetSuite delete operation failed", e); } } private void setApplicationInfo(NetSuitePortType port, ApplicationInfo applicationInfo) { // Implementation depends on your CXF version // This sets the application info in SOAP headers } }

Business Object Implementations

1. Customer Service

@Service public class CustomerService { private final NetSuiteConnector netsuiteConnector; public CustomerService(NetSuiteConnector netsuiteConnector) { this.netsuiteConnector = netsuiteConnector; } /** * Create customer in NetSuite */ public String createCustomer(CustomerDTO customerDTO) { try { Customer customer = new Customer(); // Set basic information customer.setCompanyName(customerDTO.getCompanyName()); customer.setEmail(customerDTO.getEmail()); customer.setPhone(customerDTO.getPhone()); // Set subsidiary RecordRef subsidiary = new RecordRef(); subsidiary.setInternalId(customerDTO.getSubsidiaryId()); customer.setSubsidiary(subsidiary); // Set address if (customerDTO.getAddress() != null) { CustomerAddressbook addressbook = new CustomerAddressbook(); addressbook.setDefaultShipping(true); addressbook.setDefaultBilling(true); Address address = new Address(); address.setAddr1(customerDTO.getAddress().getLine1()); address.setAddr2(customerDTO.getAddress().getLine2()); address.setCity(customerDTO.getAddress().getCity()); address.setState(customerDTO.getAddress().getState()); address.setZip(customerDTO.getAddress().getZipCode()); address.setCountry(Country._unitedStates); addressbook.setAddress(address); customer.setAddressbookList(new CustomerAddressbookList()); customer.getAddressbookList().getAddressbook().add(addressbook); } // Set custom fields if (customerDTO.getCustomFields() != null) { setCustomFields(customer, customerDTO.getCustomFields()); } WriteResponse response = netsuiteConnector.addRecord(customer); return response.getBaseRef().getInternalId(); } catch (Exception e) { throw new RuntimeException("Failed to create customer", e); } } /** * Search customers by email */ public List<Customer> findCustomersByEmail(String email) { try { CustomerSearch customerSearch = new CustomerSearch(); // Create search criteria CustomerSearchBasic searchBasic = new CustomerSearchBasic(); StringSearchField emailField = new StringSearchField(); emailField.setOperator(SearchStringFieldOperator.is); emailField.setSearchValue(email); searchBasic.setEmail(emailField); customerSearch.setBasic(searchBasic); SearchResult searchResult = netsuiteConnector.searchRecords(customerSearch, 50); List<Customer> customers = new ArrayList<>(); if (searchResult.getRecordList() != null) { for (Record record : searchResult.getRecordList().getRecord()) { if (record instanceof Customer) { customers.add((Customer) record); } } } return customers; } catch (Exception e) { throw new RuntimeException("Failed to search customers", e); } } /** * Update customer currency */ public void updateCustomerCurrency(String customerInternalId, String currencyInternalId) { try { // First get the customer ReadResponse readResponse = netsuiteConnector.getRecordById("customer", customerInternalId); Customer customer = (Customer) readResponse.getRecord(); // Update currency RecordRef currencyRef = new RecordRef(); currencyRef.setInternalId(currencyInternalId); customer.setCurrency(currencyRef); // Update customer netsuiteConnector.updateRecord(customer); } catch (Exception e) { throw new RuntimeException("Failed to update customer currency", e); } } private void setCustomFields(Customer customer, Map<String, Object> customFields) { CustomFieldList customFieldList = new CustomFieldList(); for (Map.Entry<String, Object> entry : customFields.entrySet()) { StringCustomField customField = new StringCustomField(); customField.setScriptId(entry.getKey()); customField.setValue(entry.getValue().toString()); customFieldList.getCustomField().add(customField); } customer.setCustomFieldList(customFieldList); } }

2. Sales Order Service

@Service public class SalesOrderService { private final NetSuiteConnector netsuiteConnector; public SalesOrderService(NetSuiteConnector netsuiteConnector) { this.netsuiteConnector = netsuiteConnector; } /** * Create sales order */ public String createSalesOrder(SalesOrderDTO orderDTO) { try { SalesOrder salesOrder = new SalesOrder(); // Set entity (customer) RecordRef entity = new RecordRef(); entity.setInternalId(orderDTO.getCustomerInternalId()); salesOrder.setEntity(entity); // Set transaction date salesOrder.setTranDate(Calendar.getInstance()); // Set items SalesOrderItemList itemList = new SalesOrderItemList(); for (SalesOrderItemDTO itemDTO : orderDTO.getItems()) { SalesOrderItem item = new SalesOrderItem(); RecordRef itemRef = new RecordRef(); itemRef.setInternalId(itemDTO.getItemInternalId()); item.setItem(itemRef); item.setQuantity(itemDTO.getQuantity()); item.setRate(itemDTO.getRate()); itemList.getItem().add(item); } salesOrder.setItemList(itemList); WriteResponse response = netsuiteConnector.addRecord(salesOrder); return response.getBaseRef().getInternalId(); } catch (Exception e) { throw new RuntimeException("Failed to create sales order", e); } } /** * Fulfill sales order */ public String fulfillSalesOrder(String salesOrderInternalId) { try { ItemFulfillment fulfillment = new ItemFulfillment(); // Create fulfillment from sales order RecordRef createdFrom = new RecordRef(); createdFrom.setInternalId(salesOrderInternalId); createdFrom.setType(RecordType.salesOrder); fulfillment.setCreatedFrom(createdFrom); WriteResponse response = netsuiteConnector.addRecord(fulfillment); return response.getBaseRef().getInternalId(); } catch (Exception e) { throw new RuntimeException("Failed to fulfill sales order", e); } } }

Advanced Features

1. Async Batch Processing

@Service public class NetSuiteBatchService { private final NetSuiteConnector netsuiteConnector; public NetSuiteBatchService(NetSuiteConnector netsuiteConnector) { this.netsuiteConnector = netsuiteConnector; } /** * Process multiple records in batch */ public BatchResult processBatch(List<Record> records, String operation) { try { NetSuiteService service = netsuiteConnector.createNetSuiteService(); NetSuitePortType port = service.getNetSuitePort(); Passport passport = netsuiteConnector.createPassport(); ApplicationInfo applicationInfo = netsuiteConnector.createApplicationInfo(); // Create batch request based on operation switch (operation.toLowerCase()) { case "add": return processAddBatch(port, passport, applicationInfo, records); case "update": return processUpdateBatch(port, passport, applicationInfo, records); default: throw new IllegalArgumentException("Unsupported batch operation: " + operation); } } catch (Exception e) { throw new RuntimeException("Batch processing failed", e); } } private BatchResult processAddBatch(NetSuitePortType port, Passport passport, ApplicationInfo appInfo, List<Record> records) { AddListRequest addListRequest = new AddListRequest(); addListRequest.getRecord().addAll(records); setApplicationInfo(port, appInfo); AddListResponse response = port.addList(addListRequest, passport); return processWriteResponseList(response.getWriteResponseList()); } private BatchResult processWriteResponseList(WriteResponseList responseList) { BatchResult result = new BatchResult(); for (WriteResponse response : responseList.getWriteResponse()) { if (response.getStatus().isIsSuccess()) { result.getSuccessRecords().add(response.getBaseRef().getInternalId()); } else { result.getFailedRecords().put( response.getBaseRef().getInternalId(), response.getStatus().getStatusDetail()[0].getMessage() ); } } return result; } public static class BatchResult { private List<String> successRecords = new ArrayList<>(); private Map<String, String> failedRecords = new HashMap<>(); // Getters and setters public List<String> getSuccessRecords() { return successRecords; } public Map<String, String> getFailedRecords() { return failedRecords; } } }

2. Error Handling and Retry

@Component public class NetSuiteErrorHandler { private static final Logger logger = LoggerFactory.getLogger(NetSuiteErrorHandler.class); /** * Handle NetSuite SOAP faults */ public void handleSoapFault(Exception e) { if (e instanceof com.netsuite.webservices.platform.faults.InsufficientPermissionFault) { logger.error("Insufficient permissions for NetSuite operation"); throw new SecurityException("User lacks required NetSuite permissions"); } else if (e instanceof com.netsuite.webservices.platform.faults.InvalidSessionFault) { logger.error("Invalid NetSuite session - reauthentication required"); throw new SessionExpiredException("NetSuite session expired"); } else if (e instanceof com.netsuite.webservices.platform.faults.ExceededRequestLimitFault) { logger.warn("NetSuite request limit exceeded - implementing backoff"); throw new RateLimitException("NetSuite rate limit exceeded"); } else { logger.error("NetSuite SOAP fault: {}", e.getMessage()); throw new RuntimeException("NetSuite operation failed", e); } } /** * Check if operation should be retried */ public boolean shouldRetry(Exception e, int attempt) { if (attempt >= 3) return false; return e instanceof com.netsuite.webservices.platform.faults.ExceededRequestLimitFault || e instanceof java.net.SocketTimeoutException || e instanceof javax.xml.ws.WebServiceException; } /** * Calculate retry delay with exponential backoff */ public long calculateRetryDelay(int attempt) { return (long) Math.pow(2, attempt) * 1000; // 2s, 4s, 8s } }

RESTlet Integration (Alternative)

RESTlet Client

@Service public class NetSuiteRestletClient { @Value("${netsuite.restlet.url}") private String restletUrl; @Value("${netsuite.account}") private String account; private final RestTemplate restTemplate; public NetSuiteRestletClient(RestTemplate restTemplate) { this.restTemplate = restTemplate; } /** * Call NetSuite RESTlet */ public <T> T callRestlet(String scriptId, String deployId, Object payload, Class<T> responseType) { try { String url = String.format("%s?script=%s&deploy=%s", restletUrl, scriptId, deployId); HttpHeaders headers = createRestletHeaders(); HttpEntity<Object> request = new HttpEntity<>(payload, headers); ResponseEntity<T> response = restTemplate.exchange( url, HttpMethod.POST, request, responseType); return response.getBody(); } catch (Exception e) { throw new RuntimeException("RESTlet call failed", e); } } private HttpHeaders createRestletHeaders() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); // Token-based authentication String auth = account + ":" + "your-token" + ":" + "your-token-secret"; String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes()); headers.set("Authorization", "Basic " + encodedAuth); return headers; } }

Best Practices

  1. Connection Management: Use connection pooling and proper timeout settings
  2. Error Handling: Implement comprehensive error handling for NetSuite-specific faults
  3. Batch Operations: Use batch operations for bulk data processing
  4. Pagination: Implement pagination for large search results
  5. Logging: Log all NetSuite API calls for debugging and auditing
  6. Performance: Cache frequently accessed reference data
  7. Security: Secure credentials and use token-based authentication where possible
  8. Monitoring: Monitor API usage and implement rate limiting

Configuration Tips

@Configuration public class NetSuiteConfig { @Bean public RestTemplate restTemplate() { return new RestTemplateBuilder() .setConnectTimeout(Duration.ofSeconds(30)) .setReadTimeout(Duration.ofSeconds(60)) .build(); } }

This comprehensive SuiteTalk implementation provides a solid foundation for integrating Java applications with NetSuite, covering both basic CRUD operations and advanced features like batch processing and error handling.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper