/**
* @NApiVersion 2.1
* @NScriptType UserEventScript
*/
/*******************************************************************************************************
* Corp Design
*
* CDUS-4073 : NetSuite - Adobe Commerce Two Way Customer/Contact Sync
*
* *****************************************************************************************************
*
* Author: Jobin and Jismi IT Services
*
* Date Created : 20-May-2024
*
*Description : This User Event script is developed to sync the customers and contacts created in
*NetSuite with the Adobe Commerce Website.
*
* REVISION HISTORY
*
* @version 1.0 CDUS-4073 : 20-May-2024 : Created the initial build by JJ0152
*
******************************************************************************************************/
define(['N/https', 'N/record', 'N/search', 'N/url', 'N/error', 'N/email', '../Common Library/JJ Adobe Common Library.js', '../Common Library/JJ Adobe NS Utility.js'],
function (https, record, search, url, error, email, adobeLibrary, jjUtil) {
const adobeLib = adobeLibrary.Library;
/**
* Defines the function definition that is executed before record is loaded.
* @param {Object} scriptContext
* @param {Record} scriptContext.newRecord - New record
* @param {string} scriptContext.type - Trigger type; use values from the context.UserEventType enum
* @param {Form} scriptContext.form - Current form
* @param {ServletRequest} scriptContext.request - HTTP request information sent from the browser for a client action only.
* @since 2015.2
*/
const beforeLoad = (scriptContext) => {
}
/**
* Defines the function definition that is executed before record is submitted.
* @param {Object} scriptContext
* @param {Record} scriptContext.newRecord - New record
* @param {Record} scriptContext.oldRecord - Old record
* @param {string} scriptContext.type - Trigger type; use values from the context.UserEventType enum
* @since 2015.2
*/
const beforeSubmit = (scriptContext) => {
}
/**
* Defines the function definition that is executed after record is submitted.
* @param {Object} scriptContext
* @param {Record} scriptContext.newRecord - New record
* @param {Record} scriptContext.oldRecord - Old record
* @param {string} scriptContext.type - Trigger type; use values from the context.UserEventType enum
* @since 2015.2
*/
const afterSubmit = (scriptContext) => {
//customer/contact creation context
if (scriptContext.type == scriptContext.UserEventType.CREATE || scriptContext.type == scriptContext.UserEventType.COPY || scriptContext.type == scriptContext.UserEventType.EDIT) {
try {
//code for checking if sandbox/production to avoid integration via sandbox after Sandbox refresh
// let isSandbox = jjutil.refFunction.isSandbox(); //uncomment this after moving the code to production!
// if (isSandbox) return;
let newRec = record.load({
type: scriptContext.newRecord.type,
id: scriptContext.newRecord.id,
isDynamic: false
})
// let newRec = scriptContext.newRecord;
let adobeCustomerId = newRec.getValue({ fieldId: "custentity_jj_adobe_customer_id" });
let syncCustomer = newRec.getValue({ fieldId: "custentity_jj_sync_cust_adobe_cdus_4073" });
let isInactive = newRec.getValue({ fieldId: "isinactive" });
let emailId = newRec.getValue({ fieldId: "email" });
if (!syncCustomer || isInactive || adobeCustomerId || !emailId) {
return false;
}
// if (scriptContext.newRecord.type == 'customer') {
let customerResponse = createAdobeCustomer(newRec, scriptContext.newRecord.type);
log.debug('customerResponse', customerResponse);
// }
// else if (scriptContext.newRecord.type == 'contact') {
// }
// Send Email to customer for login and reset password
if (customerResponse.status && customerResponse.adobeId) {
let recURLHyperLink = 'https://magento-817980-4546626.cloudwaysapps.com/customer/account/login/';
let url = "<a href='" + recURLHyperLink + "'>Click here</a>"
// let urllink = "<a href='" + recURLHyperLink + "'>Corp Design</a>"
let emailSubject = "Corp Design Website Registration Process"
let emailBody = 'Dear ' + customerResponse.responseObj.name + ','
+ '<br>'
+ '<br>'
+ ' We are pleased to introduce our website. Your account has been activated for Corp Design’s Website.'
+ '<br>'
+ '<br>'
+ url + ' to login.'
+ '<br>' + '<br>'
+ 'Please login with the credentials given below and change your password.'
+ '<br>' + '<br>'
+ '<br>'
+ 'Email: ' + customerResponse.userName
+ '<br>'
+ '<br>'
+ 'Password: ' + customerResponse.pwd
+ '<br>'
+ '<br>'
+ '<br>'
+ 'Thank you!'
+ '<br>'
+ '<br>'
+ 'Your team @ Corp Design'
+ '<br>'
+ '<br>'
log.debug('newRec.id', newRec)
if (customerResponse.userName) {
email.send({
author: 5038,
recipients: customerResponse.userName,
subject: emailSubject,
body: emailBody,
relatedRecords: {
entityId: newRec.id
}
});
newRec.setValue('custentity_jj_login_email_sent', true);
newRec.save();
}
}
} catch (e) {
log.error("error @ beforeSubmit", e);
}
}
}
/**
* @des address update in Website
* @param oldRec
* @param newRec
*/
function addressUpdateInAdobe(custMagentoId, customerRec) {
try {
//get the line count of address sublists in both oldRecord and newRecord contexts
log.debug("insid address creation");
var recId = customerRec.id;
var recType = customerRec.type;
log.debug("recType", recType);
let custPhone = customerRec.getValue('phone');
var numLine = customerRec.getLineCount({
sublistId: 'addressbook'
});
var addr = [];
var check = [];
// get values of address subrecord fields
for (var i = 0; i < numLine; i++) {
let addrObj = {};
var defaultshipping = customerRec.getSublistValue({
fieldId: 'defaultshipping',
sublistId: 'addressbook',
line: i
})
var defaultbilling = customerRec.getSublistValue({
fieldId: 'defaultbilling',
sublistId: 'addressbook',
line: i
})
var address = customerRec.getSublistSubrecord({
sublistId: 'addressbook',
fieldId: 'addressbookaddress',
line: i
});
var country = address.getValue({
fieldId: 'country',
});
var phone = address.getValue({
fieldId: 'addrphone',
}) || custPhone;
var attention = address.getValue({
fieldId: 'attention'
});
var addressee = address.getValue({
fieldId: 'addressee'
});
var name;
if (attention)
name = attention;
else name = addressee;
var name_arr = name.split(" ");
log.debug("name_arr", name_arr);
if (name_arr.length > 1) {
var lastname = name_arr.pop();
var firstname = name_arr.join(" ");
} else {
firstname = name;
lastname = " ";
}
var address1 = address.getValue({
fieldId: 'addr1'
});
var state = address.getValue({
fieldId: 'dropdownstate'
});
var address2 = address.getValue({
fieldId: 'addr2'
});
var city = address.getValue({
fieldId: 'city'
});
var zip = address.getValue({
fieldId: 'zip'
});
if (zip && state) {
if (name == attention)
var street = [addressee + " " + address1 + " " + address2];
else var street =
[address1 + " " + address2];
var defaultShipping = "0";
var defaultBilling = "0";
if (defaultshipping) defaultShipping = "1";
if (defaultbilling) defaultBilling = "1";
addrObj = {
"customer_id": custMagentoId,
"firstname": firstname,
"lastname": lastname,
"telephone": phone,
"street": street,
"company": "test",
"City": city,
"Post_Code": zip,
"Country": country,
"State": state,
"set_is_default_shipping": defaultShipping,
"set_is_default_billing": defaultBilling
}
log.debug("addrObj", addrObj)
// send API request for adress creation
var accountDetails = adobeLib.apiConfigfileSearch();
var hostname = accountDetails.domain;
var apiCredentials;
var response;
var headerObj = {
"Authorization": `Bearer ${accountDetails.access_token}`,
"Accept": "application/json",
"X-Requested-With": "XMLHttpRequest"
}
apiCredentials = adobeLib.ADOBE_API_REQUESTS.CREATE_CUSTOMER_ADDRESS;
var addressUpdateAPI = hostname + apiCredentials;
response = https.post({
url: addressUpdateAPI,
headers: headerObj,
body: JSON.stringify(addrObj)
});
// log.debug("response",response);
log.debug("response.body", response.body)
var responseBody = response.body;
// log.debug("responseBody",responseBody);
responseBody = JSON.parse(responseBody);
var newAdobeAddressId = Number(responseBody.params);
log.debug("newAdobeAddressId", newAdobeAddressId);
if (response.code == 200) {
// log.debug("inside set value");
var subrec = customerRec.getSublistSubrecord({
sublistId: 'addressbook',
fieldId: 'addressbookaddress',
line: i
});
subrec.setValue({
fieldId: "custrecord_jj_adobe_address_id",
value: Number(newAdobeAddressId),
ignoreFieldChange: true
});
}
}
}
} catch (e) {
log.error("error @ addresscreation", e);
return false;
}
}
/**
* Function to fetch the customer details to pass to adobe commerce to create a customer record.
* @param {Number} newRecord
* @returns {Object} customerDetails
*/
function fetchCustomerData(newRecord, recordType) {
try {
//getting customer Details to send to magento
let name = recordType == 'customer' ? newRecord.getValue({ fieldId: "companyname" }) : newRecord.getValue({ fieldId: "entityid" });
let firstName, lastName;
let nameArr = name.split(" ");
if (nameArr.length > 1) {
lastName = nameArr.pop();
firstName = nameArr.join(" ");
}
else {
firstName = name;
lastName = " ";
}
let emailId = newRecord.getValue({ fieldId: "email" });
firstName = firstName.replace(/[$@%()/&,]/g, '')
lastName = lastName.replace(/[$@%()/&,]/g, '');
log.debug("firstname & lastname", firstName + '&' + lastName);
let custStatus = (newRecord.getValue({ fieldId: "isinactive" }) == 'T') ? 0 : 1;
let id = newRecord.id;
log.debug('customerId', id)
// let customer = record.load({
// type: "customer",
// id: id,
// isDynamic: false,
// });
let salesRep, customerContact, csrId, terms, shipMethod, shipMethodId, priceLevelInCustomerRec, isSpecialCustomer, specialCustomer, nsWarehouseId, customerStatus;
let parentName, parentAdobeId;
if (recordType == 'customer') {
customerContact = 1;
salesRep = newRecord.getValue('salesrep');
csrId = newRecord.getValue('custentity_jj_customerservicerep');
terms = newRecord.getValue('terms');
shipMethodId = newRecord.getValue('shippingitem') || 912;
shipMethod = shipMethodId ? search.lookupFields({
type : 'shipitem',
id: Number(shipMethodId),
columns: ['itemid']
}).itemid : "Pick Up";
priceLevelInCustomerRec = newRecord.getValue('pricelevel');
isSpecialCustomer = newRecord.getValue({ fieldId: "custentity_jj_deposite_not_required" });
specialCustomer = isSpecialCustomer ? 1 : 0;
let phone = newRecord.getValue('phone');
nsWarehouseId = newRecord.getValue('custentity_preferred_shipping_warehouse');
customerStatus = 1;
}
else if (recordType == 'contact') {
customerContact = 0;
let parentNSId = newRecord.getValue('company');
var fieldLookUp = search.lookupFields({
type: search.Type.CUSTOMER,
id: Number(parentNSId),
columns: ['custentity_preferred_shipping_warehouse', 'shippingitem', 'custentity_jj_price_level_ship_methods', 'companyname', 'custentity_jj_adobe_customer_id', 'salesrep', 'terms', 'pricelevel', 'custentity_jj_deposite_not_required', 'custentity_jj_customerservicerep']
});
log.debug('fieldLookUp', fieldLookUp)
salesRep = fieldLookUp.salesrep[0]?.value;
csrId = fieldLookUp.custentity_jj_customerservicerep[0]?.value;
terms = fieldLookUp.terms[0]?.text || '';
shipMethod = fieldLookUp.shippingitem[0]?.text || "Pick Up";
shipMethodId = fieldLookUp.shippingitem[0]?.value || 912;
priceLevelInCustomerRec = fieldLookUp.pricelevel[0]?.value;
pickUpPriceLevel = 1;
isSpecialCustomer = fieldLookUp.custentity_jj_deposite_not_required;
specialCustomer = isSpecialCustomer ? 1 : 0;
nsWarehouseId = fieldLookUp.custentity_preferred_shipping_warehouse[0]?.value;
customerStatus = 0;
parentName = fieldLookUp.companyname;
parentAdobeId = fieldLookUp.custentity_jj_adobe_customer_id;
}
// get price level for customer from price level mapping list
let nsAdobePriceLevel = jjUtil.savedSearch.fetchPriceLevelFromMappingList(shipMethodId, id);
// if price level mapping is not available, take value from pricelevel field in customer record
// else if pricelevel field value is not available , use list price
if (nsAdobePriceLevel == 0 || nsAdobePriceLevel == 'false' || nsAdobePriceLevel == false) {
nsAdobePriceLevel = priceLevelInCustomerRec || 1;
}
//get the magento ID of price level using the custom record set up for netsuite - magento price level sync
let priceLevelDetails = jjUtil.refFunction.priceRecordSearch(nsAdobePriceLevel);
let priceLevelId = priceLevelDetails[0].MagentoId;
//get magento location source code
let locationSourceCode = search.lookupFields({
type: search.Type.LOCATION,
id: Number(nsWarehouseId),
columns: ['custrecord_jj_adobe_source_code']
}).custrecord_jj_adobe_source_code;
//get the magento sales rep ID
let adobeSalesRepId = search.lookupFields({
type: search.Type.EMPLOYEE,
id: Number(salesRep),
columns: ['custentity_jj_adobe_sales_rep_id']
}).custentity_jj_adobe_sales_rep_id;
//get the magento csr ID
let adobeCSRId = 0;
if (csrId) {
adobeCSRId = search.lookupFields({
type: search.Type.EMPLOYEE,
id: Number(csrId),
columns: ['custentity_jj_adobe_sales_rep_id']
}).custentity_jj_adobe_sales_rep_id || 0;
}
let customAttributes = [
{ "attribute_code": "status_id", "value": custStatus },
{ "attribute_code": "customer_type", "value": customerContact },
{ "attribute_code": "is_approved", "value": "1" },
{ "attribute_code": "company_name", "value": name },
{ "attribute_code": "shipping_method", "value": shipMethod },
{ "attribute_code": "terms_available", "value": terms },
{ "attribute_code": "special_customer", "value": specialCustomer },
{ "attribute_code": "warehouse_id", "value": locationSourceCode },
{ "attribute_code": "netsuite_id", "value": newRecord.id }];
if (adobeSalesRepId) {
customAttributes.push({
"attribute_code": "sales_rep_id",
"value": adobeSalesRepId
});
}
if (adobeCSRId) {
customAttributes.push({
"attribute_code": "csr_id",
"value": adobeCSRId
});
}
if (recordType == 'contact') {
customAttributes.push({
"attribute_code": "dealer_name",
"value": parentName
});
customAttributes.push({
"attribute_code": "parent_id",
"value": parentAdobeId
});
}
let customerDetails = {
"group_id": Number(priceLevelId),
"email": emailId,
"firstname": firstName,
"lastname": lastName,
"extension_attributes": {},
"custom_attributes": customAttributes
};
return { customerDetails: customerDetails, shipMethodId: shipMethodId, nsAdobePriceLevel: nsAdobePriceLevel };
} catch (e) {
log.error("Error @ fetchCustomerData", e);
return { customerDetails: '', shipMethodId: '', nsAdobePriceLevel: '' };
}
}
/**
* @description function for creating custom record for customer
* @param responseObj - API response body for customer
* @returns {cutom record Id}
*/
function LoadCreateCustomRecord(responseObj) {
try {
let customRec
if (responseObj.customerRec) {
customRec = record.load({
type: "customrecord_jj_adobe_customer_sync_1105",
id: responseObj.customerRec,
isDynamic: true
});
}
else {
customRec = record.create({
type: "customrecord_jj_adobe_customer_sync_1105",
isDynamic: true
});
}
customRec.setValue({
fieldId: "custrecord_jj_customer_id",
value: responseObj.adobeId
});
customRec.setValue({
fieldId: "name",
value: responseObj.name
});
customRec.setValue({
fieldId: "custrecord_jj_adobe_api_response_body",
value: JSON.stringify(responseObj.responseBody)
});
customRec.setValue({
fieldId: "custrecord_jj_customer_in_netsuite",
value: responseObj.id
});
customRec.setValue({
fieldId: "custrecord_jj_customer_type",
value: responseObj.type
});
customRec.setValue({
fieldId: "custentity_jj_customer_source_cdus_4073",
value: 1
});
let customRecId = customRec.save({ ignoreMandatoryFields: true });
return customRecId;
} catch (e) {
log.debug("error@createCustomRec", e);
return false;
}
}
/**
* @description function for generating password
* @returns {*}
*/
function generatePassword(length) {
try {
/**
* sets of charachters
*/
let upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
let lower = 'abcdefghijklmnopqrstuvwxyz';
let digit = '0123456789';
let specialChar = '?=.*[!@#$%^&/*]';
let all = upper + lower + digit + specialChar;
/**
* generate random integer not greater than `max`
*/
function rand(max) {
return Math.floor(Math.random() * max)
}
/**
* generate random character of the given `set`
*/
function random(set) {
return set[rand(set.length - 1)]
}
/**
* generate an array with the given `length`
* of characters of the given `set`
*/
function generate(length, set) {
let result = []
while (length--) result.push(random(set))
return result
}
/**
* shuffle an array randomly
*/
function shuffle(arr) {
let result = []
while (arr.length) {
result = result.concat(arr.splice(rand[arr.length - 1]))
}
return result
}
/**
* do the job
*/
let result = [] // we need to ensure we have some characters
result = result.concat(generate(1, upper)) // 1 upper case
result = result.concat(generate(1, lower)) // 1 lower case
result = result.concat(generate(1, digit)) // 1 digit
result = result.concat(generate(1, specialChar)) // 1 special char
result = result.concat(generate(length - 3, all)) // remaining - whatever
let pwd = shuffle(result).join('') // shuffle and make a string
return pwd;
} catch (e) {
log.error("error @ generate password", e);
return false;
}
}
/**
* @description function for checking
* @param custAdobeId - API response body for customer
* @returns {cutom record Id}
*/
function fetchCustomRec(custAdobeId) {
try {
let customRecSearchObj = search.create({
type: "customrecord_jj_adobe_customer_sync_1105",
filters:
[
["custrecord_jj_customer_id", "is", custAdobeId],
"AND",
["custrecord_jj_customer_in_netsuite", "isempty", true]
],
columns:
[
search.createColumn({
name: "name",
sort: search.Sort.ASC,
label: "Name"
}),
search.createColumn({ name: "internalid", label: "Internal ID" }),
search.createColumn({ name: "custrecord_jj_customer_id", label: "Customer Adobe ID" }),
search.createColumn({ name: "custrecord_jj_customer_in_netsuite", label: "Customer" }),
search.createColumn({ name: "custrecord_jj_customer_type", label: "Type" })
]
});
let internalId;
let searchResultCount = customRecSearchObj.runPaged().count;
if (searchResultCount > 0) {
customRecSearchObj.run().each(function (result) {
internalId = result.getValue({
name: "internalid"
});
return false;
});
return internalId;
}
else {
return false;
}
} catch (e) {
log.debug("error@fetchCustomRec", e);
return false;
}
}
/**
* Function to create a customer record in Adobe Commerce corresponding to a NetSuite customer
* @param {Integer} newRecord
*/
function createAdobeCustomer(newRecord, recordType) {
try {
let customerDetails = fetchCustomerData(newRecord, recordType);
log.debug('customerDetails', customerDetails.customerDetails)
if (Object.entries(customerDetails.customerDetails).length > 0) {
log.debug("customerDetails", customerDetails);
// code to create a new cutsomer in magento
let apiCredentials = adobeLib.ADOBE_API_REQUESTS.CREATE_CUSTOMER;
let pwd = generatePassword(8);
log.debug('password', pwd)
let BODY_TO_SEND = {
"customer": customerDetails.customerDetails,
"password": pwd
}
let Response = adobeLib.requestMagento(
apiCredentials,
"POST",
BODY_TO_SEND
);
let responsebody = Response[2];
let responsecode = Response[1];
log.debug("responsecode", responsecode + " " + responsebody);
let currentDate = jjUtil.refFunction.changeTimeZone();
let adobeId = null;
let customRecDetails = null;
log.debug("currentDate", currentDate);
if (responsecode == 200 || responsecode == 204 || responsecode == 201) {
//on successful sync, set back the magento ID to netsuite customer
adobeId = responsebody['id'];
log.debug("adobeId", adobeId);
if (adobeId) {
let addressUpdateSuccess = addressUpdateInAdobe(adobeId, newRecord);
newRecord.setValue({ fieldId: 'custentity_jj_adobe_customer_id', value: adobeId });
newRecord.setValue('custentity_jj_cut_update_status', 'SUCCESSFULLY CREATED');
newRecord.setValue('custentity_jj_error_on_sync', '');
newRecord.setValue('custentity_jj_cust_updated_on', currentDate);
newRecord.setValue('custentity_jj_approved_customer', true);
newRecord.setValue('custentity_jj_prefer_ship_method', customerDetails.shipMethodId);
newRecord.setValue('custentity_jj_price_level_ship_methods', customerDetails.nsAdobePriceLevel);
newRecord.setValue('custentity_jj_customer_source_cdus_4073', 1);
customRecDetails = fetchCustomRec(adobeId);
}
}
else {
newRecord.setValue('custentity_jj_cut_update_status', 'FAILURE');
newRecord.setValue('custentity_jj_error_on_sync', responsebody);
newRecord.setValue('custentity_jj_cust_updated_on', currentDate);
}
//create the custom record entry for error handling
// check for entry in custom record
log.debug("customRecDetails", customRecDetails);
let responseObj = {
id: newRecord.id, adobeId: adobeId, responsecode: responsecode, responsebody: responsebody,
customerRec: customRecDetails, name: customerDetails.customerDetails.firstname + " " + customerDetails.customerDetails.lastname, type: recordType == 'customer'?2:4
}
LoadCreateCustomRecord(responseObj);
return { status: true, userName: customerDetails.customerDetails.email, pwd: pwd, adobeId: adobeId, responseObj: responseObj }
}
else {
log.debug("*** Unable to fetch the customer data ***");
return { status: false, userName: '', pwd: '', adobeId: null, responseObj: {} }
}
} catch (e) {
log.error("Error @ createAdobeCustomer", e.message);
return { status: false, userName: '', pwd: '', adobeId: null, responseObj: {} }
}
}
return { beforeLoad, beforeSubmit, afterSubmit }
});