Created
July 8, 2020 18:45
-
-
Save jmbeach/01b60e3ad75ce732ca3db1eddcc88794 to your computer and use it in GitHub Desktop.
Custom Merge Logic for NetSuite
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @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 | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @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 | |
} | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
*@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 | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @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("help")"' + | |
'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' }); | |
} | |
} | |
} | |
} | |
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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