Skip to content

Instantly share code, notes, and snippets.

@jmbeach
Created July 8, 2020 18:45
Show Gist options
  • Save jmbeach/01b60e3ad75ce732ca3db1eddcc88794 to your computer and use it in GitHub Desktop.
Save jmbeach/01b60e3ad75ce732ca3db1eddcc88794 to your computer and use it in GitHub Desktop.
Custom Merge Logic for NetSuite
// Client script Merge clicked fragment
MergeClicked = function () {
var id = document.getElementById('id').value;
window.location.href = '/app/site/hosting/scriptlet.nl?script=customscript_customer_merge_page&deploy=1&customerid=' + id;
};
/**
* @NApiVersion 2.0
* @NScriptType plugintypeimpl
*/
define(function () {
/**
* @param {NetSuiteQueryModule} query
* @param {string} fileName
*/
var getFileDataByFileName = function (query, fileName) {
var scriptQuery = query.create({
type: query.Type.FILE
});
scriptQuery.condition = scriptQuery.createCondition({
fieldId: 'name',
operator: query.Operator.START_WITH,
values: fileName
});
scriptQuery.columns = [
scriptQuery.createColumn({ fieldId: 'id' }),
scriptQuery.createColumn({ fieldId: 'name' }),
];
var scriptQueryResults = scriptQuery.run();
var scriptData = scriptQueryResults.results[0].values;
return {
id: scriptData[0]
};
}
return {
getFileDataByFileName: getFileDataByFileName
}
});
/**
* @NApiVersion 2.x
* @NScriptType ScheduledScript
*/
define(['N/runtime', 'N/plugin', 'N/task', 'N/search', 'N/https', 'N/log'],
/**
* @param {NetSuiteRuntime} runtime
* @param {NetSuitePluginModule} plugin
* @param {NetSuiteTaskModule} task
* @param {NetSuiteSearchModule} search
* @param {NetSuiteHttpModule} https
* @param {NetSuiteLogModule} log
*/
function (runtime, plugin, task, search, https, log) {
return {
execute: function (context) {
/** @type {NetSuiteSimpler} */
var NetSuiteSimpler = plugin.loadImplementation({ type: 'customscript_netsuite_simpler_type' });
var mergeTaskId = runtime.getCurrentScript().getParameter({
name: 'custscript_merge_task_id'
});
var customerIdMaster = runtime.getCurrentScript().getParameter({
name: 'custscript_customer_id_master'
});
var customerIdSource = runtime.getCurrentScript().getParameter({
name: 'custscript_customer_id_source'
});
var debugString = 'Merge task ID: ' + mergeTaskId + '|Customer ID Master: ' + customerIdMaster + '|Customer ID Source: ' + customerIdSource;
log.debug({ title: 'Start Merge Task', details: debugString});
var taskStatus = task.checkStatus({
taskId: mergeTaskId
});
var startTime = new Date();
while (taskStatus == task.TaskStatus.COMPLETE || taskStatus === task.TaskStatus.PROCESSING) {
taskStatus = task.checkStatus({
taskId: mergeTaskId
});
// if checking for over an hour, probably have an infinte loop issue
if (startTime.getTime() - (new Date().getTime()) > 1000 * 60 * 60) {
log.error({ title: 'Merge polling exceeded max time', details: debugString });
return;
}
}
log.debug({ title: 'Polling Complete', details: 'Task Status: ' + JSON.stringify(taskStatus) });
if (taskStatus === task.TaskStatus.FAILED) {
log.error({ title: 'Merge Failed', details: debugString });
return;
}
// Do your custom logic here
}
}
});
/**
*@NApiVersion 2.0
*@NScriptType ClientScript
*/
// client script for merge page
define(
function () {
function onCancelClick() {
window.history.back();
}
function onMergeClick() {
if (!document.getElementById('hddn_custpage_customerselect1').value) {
alert('Please enter value for MASTER.');
return false;
}
if (!confirm('This action is irreversible. Are you sure you want to merge these entities?')) {
return false;
}
var id = document.getElementById('hddn_custpage_customerselect1').value;
var select = document.querySelector('div[data-name="custpage_customerselect"]');
var options = JSON.parse(select.dataset.options);
var text = options.find(function (x) { return x.value === id }).text;
document.getElementById('toentity').value = id;
document.getElementById('toentity_display').value = text;
var targetId = document.getElementById('id').value;
var submit = new XMLHttpRequest();
var handleError = function () {
alert('Unknown error occurred while merging customers.');
}
submit.addEventListener('load', function (e) {
if (submit.status !== 200) {
handleError();
return;
}
window.onbeforeunload = function () {};
window.location.href = '/app/common/entity/duplicatemanagement/dupejob.nl';
});
submit.addEventListener('error', handleError);
// submits to the suitelet. Suitelet kicks off scheduled task
submit.open('POST', '');
submit.setRequestHeader('Content-Type', 'application/json');
submit.send(JSON.stringify({
masterId: id,
targetId: targetId
}));
}
function pageInit(context) {
showAlertBox('alert_box_1680', 'Irreversible data change', 'All linked records, including activities, transactions, messages, files, cases, and contacts will be moved from the duplicate into the master record.<br><br>Merging records will change the data within the database and the duplicate will be deleted. This operation can also change entity information on historical transactions and may also impact your tax reports.<br><br>To reduce the risk of unintended results, it is strongly recommended that you test the merge in your sandbox account.', 2, null, null, null);
document.getElementById('custpage_mergebutton').setAttribute('type', 'submit');
var customerId = document.getElementById('customerid').value;
document.getElementById('id').value = customerId;
document.getElementById('type').value = 'CustJob';
document.forms[0].action = '/app/common/entity/manageduplicates.nl?frame=be&submit=true&mopt=mim&id=' + customerId;
removeSourceCustomerFromSelect();
document.forms[0].setAttribute('onsubmit', 'mod.onMergeClick');
}
function removeSourceCustomerFromSelect() {
/** @type {HTMLElement} */
var select = document.querySelector('div[data-name="custpage_customerselect"]');
var options = JSON.parse(select.dataset.options);
var customerId = document.getElementById('customerid').value;
options = options.filter(function (opt) {
return opt.value !== customerId;
});
NS.jQuery(select).data('options', options);
var dpd = getDropdown(document.getElementById('hddn_custpage_customerselect1'));
dpd.textArray = options.map(function (x) { return x.text; });
dpd.valueArray = options.map(function (x) { return x.value; });
}
return {
onCancelClick: onCancelClick,
onMergeClick: onMergeClick,
pageInit: pageInit
}
});
/**
* @NApiVersion 2.0
* @NScriptType Suitelet
*/
// Suitelet expects you to pass the customer ID being edited into the URL parameter "customerid". Needs ID "customscript_customer_merge_page"
define(['N/ui/serverWidget', 'N/query', 'N/search', 'N/log', 'N/task', 'N/plugin'],
/**
* @param {ServerWidget} serverWidget
* @param {NetSuiteQueryModule} query
* @param {NetSuiteSearchModule} search
* @param {NetSuiteLogModule} log
* @param {NetSuiteTaskModule} task
* @param {NetSuitePluginModule} plugin
*/
function (serverWidget, query, search, log, task, plugin) {
var getCustomerData = function (customerId) {
var contactQuery = query.create({
type: query.Type.CUSTOMER
});
contactQuery.condition = contactQuery.createCondition({
fieldId: 'id',
operator: query.Operator.EQUAL,
values: customerId
});
contactQuery.columns = [
contactQuery.createColumn({ fieldId: 'entityid' }),
];
var resultSet = contactQuery.run();
log.debug({ title: 'customer data result', details: resultSet });
var queryData = resultSet.results[0].values;
return {
entityId: queryData[0]
};
}
return {
onRequest: /** @param {NetSuiteRequestContext} context */ function (context) {
if (context.request.method === 'GET') {
/** @type {NetSuiteSimpler} */
var NetSuiteSimpler = plugin.loadImplementation({ type: 'customscript_netsuite_simpler_type' });
var form = serverWidget.createForm({ title: 'Merge Customer' });
var customerId = context.request.parameters.customerid;
var constants = NetSuiteSimpler.getConstants(search);
form.clientScriptFileId = NetSuiteSimpler.getFileDataByFileName(query, 'Suitelet_CustomerMergePageClientScript.js').id;
log.debug({ title: 'client script file id', details: form.clientScriptFileId });
form.addButton({
id: 'custpage_mergebutton',
label: 'Merge',
functionName: 'onMergeClick'
});
form.addButton({
id: 'custpage_cancelbutton',
label: 'Cancel',
functionName: 'onCancelClick'
});
var css = form.addField({
id: 'custpage_css',
label: 'css',
type: serverWidget.FieldType.INLINEHTML
});
css.defaultValue = '<style>'
+ '#fg_custpage_fieldGroup1, #fg_custpage_fieldGroup2 { display: none; }\n'
+ '.uir-field-widget:not(.always-visible) > a#custpage_customerselect_popup_new { display: none !important; }\n'
+ '</style>';
var fieldGroup1 = form.addFieldGroup({
id: 'custpage_fieldGroup1',
label: 'Test'
});
fieldGroup1.isCollapsable = false;
/* There's probably a better way to add the fields below and make them match
* NetSuite's style, but I don't know how. */
var sourceLabel = form.addField({
id: 'custpage_dupliacatehelpfield',
label: 'defaultvalue',
type: serverWidget.FieldType.INLINEHTML,
container: 'custpage_fieldGroup1'
});
var hiddenFields = form.addField({
id: 'custpage_hiddenfields',
label: 'hiddenfields',
type: serverWidget.FieldType.INLINEHTML
});
hiddenFields.defaultValue = '<input id="customerid" type="hidden" value="' + customerId + '" />'
+ '<input id="manualmerge" name="manualmerge" type="hidden" value="T" />'
+ '<input id="submitter" name="submitter" type="hidden" value="Merge" />'
+ '<input id="toentity" name="toentity" type="hidden" />'
+ '<input id="guidesApiUrl" name="guidesApiUrl" type="hidden" value="' + constants.ironGuidesApiUrl + '" />'
+ '<input id="toentity_display" name="toentity_display" type="hidden" />';
var customerData = getCustomerData(customerId);
sourceLabel.defaultValue = '<span id="fromentity_fs_lbl_uir_label" class="smallgraytextnolink uir-label ">' +
'<span id="fromentity_fs_lbl" class="smallgraytextnolink" style="">' +
'<a tabindex="-1" title="What\'s this?" href="javascript:void(&quot;help&quot;)"' +
'style="cursor:help"' +
'onclick="return nlFieldHelp(\'Field Help\', \'fromentity\', this, \'custjob\', \'LIST_CUSTJOB_fromentity_NA\', \'NA\', \'-1\', \'UI\', \'TRAN_ENTITYMERGE\', \'652931\', \'RECORD\', \'F\', \'APP:PAGEMESSAGE:MERGE_1\', \'\', \'APP:FORMLABEL:DUPLICATE\', \'\')"' +
'class="smallgraytextnolink"' +
'onmouseover="this.className=\'smallgraytext\'; return true;"' +
'onmouseout="this.className=\'smallgraytextnolink\'; ">Duplicate</a>' +
'</span>' +
'</span>' +
'<span class="uir-field inputreadonly">' +
'<span class="inputreadonly"><a class="dottedlink" href="/app/common/entity/entity.nl?id=' + customerId + '">' + customerData.entityId + '</a></span>' +
'</span>';
var fieldGroup2 = form.addFieldGroup({
id: 'custpage_fieldGroup2',
label: 'Test'
});
fieldGroup2.isCollapsable = false;
var customerSelectField = form.addField({
id: 'custpage_customerselect',
label: 'MASTER',
type: serverWidget.FieldType.SELECT,
source: 'Customer',
container: 'custpage_fieldGroup2'
});
customerSelectField.updateBreakType({ breakType: serverWidget.FieldBreakType.STARTCOL });
customerSelectField.isMandatory = true;
context.response.writePage(form);
}
else {
var body = JSON.parse(context.request.body);
var masterId = body.masterId;
var targetId = body.targetId;
var mergeTask = task.create({
// entity deduplication means merge
taskType: task.TaskType.ENTITY_DEDUPLICATION,
entityType: task.DedupeEntityType.CUSTOMER,
masterRecordId: masterId,
masterSelectionMode: task.MasterSelectionMode.SELECT_BY_ID,
dedupeMode: task.DedupeMode.MERGE,
recordIds: [targetId]
});
var mergeTaskId = mergeTask.submit();
var scheduledTaskRequest = {
taskType: task.TaskType.SCHEDULED_SCRIPT,
scriptId: 'customscript_scheduledtask_customermerge',
deploymentId: 'customdeploy3',
params: {
custscript_merge_task_id: mergeTaskId,
custscript_customer_id_master: masterId,
custscript_customer_id_source: targetId
}
};
log.debug({ title: 'onMergeClick#mergeTaskId', details: JSON.stringify(scheduledTaskRequest) });
var scheduledTask = task.create(scheduledTaskRequest);
scheduledTask.submit();
context.response.write({ output: 'OK' });
}
}
}
}
);
// User Event - beforeLoad fragment (V1 syntax, would be very similar in V2)
if (type == 'edit') {
var button = form.getButton('merge');
if (button && button.setVisible) {
button.setVisible(false);
}
form.addButton('custpage_mergebutton', 'Merge', 'MergeClicked()');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment