/** * @NApiVersion 2.1 * @NScriptType Suitelet * @NModuleScope Public */ define(['N/file', 'N/https', 'N/log', 'N/query', 'N/record', 'N/render', 'N/runtime', 'N/ui/serverWidget', 'N/url'], initializeScriptModulesAndSetEntryPoint); var file, https, log, query, record, render, runtime, scriptURL, url, version = '2025.1-Moss-Stable_version'; var datatablesEnabled = true, remoteLibraryEnabled = true, rowsReturnedDefault = 50, queryFolderID = null, workbooksEnabled = false; function initializeScriptModulesAndSetEntryPoint(fileModule, httpsModule, logModule, queryModule, recordModule, renderModule, runtimeModule, serverWidgetModule, urlModule) { file = fileModule; https = httpsModule; log = logModule; query = queryModule; record = recordModule; render = renderModule; runtime = runtimeModule; serverWidget = serverWidgetModule; url = urlModule; return { onRequest: function (context) { scriptURL = url.resolveScript({ scriptId: runtime.getCurrentScript().id, deploymentId: runtime.getCurrentScript().deploymentId, returnExternalURL: false }); if (context.request.method == 'POST') { handlePostRequest(context); } else { handleGetRequest(context); } } } } function handleGetRequest(context) { if (context.request.parameters.hasOwnProperty('function')) { if (context.request.parameters['function'] == 'tablesReference') { generateTablesReferencePageHtml(context); } if (context.request.parameters['function'] == 'documentGenerate') { generateDocumentFromQueryResults(context); } } else { var form = serverWidget.createForm({ title: `SuiteQL Query Tool`, hideNavBar: true }); var htmlField = form.addField({ id: 'custpage_field_html', type: serverWidget.FieldType.INLINEHTML, label: 'HTML' }); htmlField.defaultValue = generateMainToolInterfaceHtml(); context.response.writePage(form); } } function handlePostRequest(context) { context.response.setHeader('Content-Type', 'application/json'); var requestPayload; try { requestPayload = JSON.parse(context.request.body); } catch (e) { log.error({ title: 'FATAL: Failed to parse request body', details: 'Body received: ' + context.request.body }); context.response.write(JSON.stringify({ 'error': { message: 'Invalid request from client. Could not parse JSON body. See execution log for details.' } })); return; } switch (requestPayload['function']) { case 'documentSubmit': return storeDocumentInfoInSessionForGeneration(context, requestPayload); case 'queryExecute': return executeQueryAndReturnResults(context, requestPayload); case 'sqlFileExists': return checkIfSqlFileExistsInLocalLibrary(context, requestPayload); case 'sqlFileLoad': return loadSqlFileFromLocalLibrary(context, requestPayload); case 'sqlFileSave': return saveSqlFileToLocalLibrary(context, requestPayload); case 'localLibraryFilesGet': return getLocalQueryLibraryFiles(context); case 'workbookLoad': return loadQueryFromWorkbook(context, requestPayload); case 'workbooksGet': return getListOfAvailableWorkbooks(context); default: log.error({ title: 'Payload - Unsupported Function', details: requestPayload['function'] }); } } function generateDocumentFromQueryResults(context) { try { var sessionScope = runtime.getCurrentSession(); var docInfo = JSON.parse(sessionScope.get({ name: 'suiteQLDocumentInfo' })); var moreRecords = true; var pagatedRowBegin = docInfo.rowBegin; var paginatedRowEnd = docInfo.rowEnd; var queryParams = new Array(); var records = new Array(); do { var paginatedSQL = 'SELECT * FROM ( SELECT ROWNUM AS ROWNUMBER, * FROM (' + docInfo.query + ' ) ) WHERE ( ROWNUMBER BETWEEN ' + paginatedRowBegin + ' AND ' + paginatedRowEnd + ')'; var queryResults = query.runSuiteQL({ query: paginatedSQL, params: queryParams }).asMappedResults(); records = records.concat(queryResults); if (queryResults.length < 5000) { moreRecords = false; } paginatedRowBegin = paginatedRowBegin + 5000; } while (moreRecords); var recordsDataSource = { 'records': records }; var renderer = render.create(); renderer.addCustomDataSource({ alias: 'results', format: render.DataSource.OBJECT, data: recordsDataSource }); renderer.templateContent = docInfo.template; if (docInfo.docType == 'pdf') { let renderObj = renderer.renderAsPdf(); let pdfString = renderObj.getContents(); context.response.setHeader('Content-Type', 'application/pdf'); context.response.write(pdfString); } else { let htmlString = renderer.renderAsString(); context.response.setHeader('Content-Type', 'text/html'); context.response.write(htmlString); } } catch (e) { log.error({ title: 'generateDocumentFromQueryResults Error', details: e }); context.response.write('Error: ' + e); } } function storeDocumentInfoInSessionForGeneration(context, requestPayload) { try { var responsePayload; var sessionScope = runtime.getCurrentSession(); sessionScope.set({ name: 'suiteQLDocumentInfo', value: JSON.stringify(requestPayload) }); responsePayload = { 'submitted': true } } catch (e) { log.error({ title: 'storeDocumentInfoInSessionForGeneration Error', details: e }); responsePayload = { 'error': e } } context.response.write(JSON.stringify(responsePayload, null, 5)); } function executeQueryAndReturnResults(context, requestPayload) { try { var responsePayload; var moreRecords = true; var records = new Array(); var totalRecordCount = 0; var queryParams = new Array(); var paginatedRowBegin = requestPayload.rowBegin; var paginatedRowEnd = requestPayload.rowEnd; var nestedSQL = requestPayload.query + "\n"; if ((requestPayload.viewsEnabled) && (queryFolderID !== null)) { var pattern = /(?:^|\s)\#(\w+)\b/ig; var views = nestedSQL.match(pattern); if ((views !== null) && (views.length > 0)) { for (let i = 0; i < views.length; i++) { view = views[i].replace(/\s+/g, ''); viewFileName = view.substring(1, view.length) + '.sql'; var sql = 'SELECT ID FROM File WHERE ( Folder = ? ) AND ( Name = ? )'; var queryResults = query.runSuiteQL({ query: sql, params: [queryFolderID, viewFileName] }); var files = queryResults.asMappedResults(); if (files.length == 1) { var fileObj = file.load({ id: files[0].id }); nestedSQL = nestedSQL.replace(view, '( ' + fileObj.getContents() + ' ) AS ' + view.substring(1, view.length)); } else { throw { 'name:': 'UnresolvedViewException', 'message': 'Unresolved View ' + viewFileName } } } } } let beginTime = new Date().getTime(); if (requestPayload.paginationEnabled) { do { var paginatedSQL = 'SELECT * FROM ( SELECT ROWNUM AS ROWNUMBER, * FROM ( ' + nestedSQL + ' ) ) WHERE ( ROWNUMBER BETWEEN ' + paginatedRowBegin + ' AND ' + paginatedRowEnd + ')'; var queryResults = query.runSuiteQL({ query: paginatedSQL, params: queryParams }).asMappedResults(); records = records.concat(queryResults); if (queryResults.length < 5000) { moreRecords = false; } paginatedRowBegin = paginatedRowBegin + 5000; } while (moreRecords); } else { records = query.runSuiteQL({ query: nestedSQL, params: queryParams }).asMappedResults(); } let elapsedTime = (new Date().getTime() - beginTime); responsePayload = { 'records': records, 'elapsedTime': elapsedTime } if (requestPayload.returnTotals) { if (records.length > 0) { var paginatedSQL = 'SELECT COUNT(*) AS TotalRecordCount FROM ( ' + nestedSQL + ' )'; var queryResults = query.runSuiteQL({ query: paginatedSQL, params: queryParams }).asMappedResults(); responsePayload.totalRecordCount = queryResults[0].totalrecordcount; } } } catch (e) { log.error({ title: 'executeQueryAndReturnResults Error', details: e }); responsePayload = { 'error': e } } context.response.write(JSON.stringify(responsePayload, null, 5)); } function getLocalQueryLibraryFiles(context) { var responsePayload; var sql = ` SELECT ID, Name, Description FROM File WHERE ( Folder = ? ) ORDER BY Name `; var queryResults = query.runSuiteQL({ query: sql, params: [queryFolderID] }); var records = queryResults.asMappedResults(); if (records.length > 0) { responsePayload = { 'records': records }; } else { responsePayload = { 'error': 'No SQL Files' }; } context.response.write(JSON.stringify(responsePayload, null, 5)); } function checkIfSqlFileExistsInLocalLibrary(context, requestPayload) { var responsePayload; var sql = ` SELECT ID FROM File WHERE ( Folder = ? ) AND ( Name = ? ) `; var queryResults = query.runSuiteQL({ query: sql, params: [queryFolderID, requestPayload.filename] }); var records = queryResults.asMappedResults(); if (records.length > 0) { responsePayload = { 'exists': true }; } else { responsePayload = { 'exists': false }; } context.response.write(JSON.stringify(responsePayload, null, 5)); } function loadSqlFileFromLocalLibrary(context, requestPayload) { var responsePayload; try { var fileObj = file.load({ id: requestPayload.fileID }); responsePayload = {} responsePayload.file = fileObj; responsePayload.sql = fileObj.getContents(); } catch (e) { log.error({ title: 'loadSqlFileFromLocalLibrary Error', details: e }); responsePayload = { 'error': e } } context.response.write(JSON.stringify(responsePayload, null, 5)); } function saveSqlFileToLocalLibrary(context, requestPayload) { var responsePayload; try { var fileObj = file.create({ name: requestPayload.filename, contents: requestPayload.contents, description: requestPayload.description, fileType: file.Type.PLAINTEXT, folder: queryFolderID, isOnline: false }); var fileID = fileObj.save(); responsePayload = {} responsePayload.fileID = fileID; } catch (e) { log.error({ title: 'saveSqlFileToLocalLibrary Error', details: e }); responsePayload = { 'error': e } } context.response.write(JSON.stringify(responsePayload, null, 5)); } function loadQueryFromWorkbook(context, requestPayload) { var responsePayload; try { var loadedQuery = query.load({ id: requestPayload.scriptID }); responsePayload = {} responsePayload.sql = loadedQuery.toSuiteQL().query; } catch (e) { log.error({ title: 'loadQueryFromWorkbook Error', details: e }); responsePayload = { 'error': e } } context.response.write(JSON.stringify(responsePayload, null, 5)); } function getListOfAvailableWorkbooks(context) { var responsePayload; var sql = ` SELECT ScriptID, Name, Description, BUILTIN.DF( Owner ) AS Owner FROM UsrSavedSearch ORDER BY Name `; var queryResults = query.runSuiteQL({ query: sql, params: [] }); var records = queryResults.asMappedResults(); if (records.length > 0) { responsePayload = { 'records': records }; } else { responsePayload = { 'error': 'No Workbooks' }; } context.response.write(JSON.stringify(responsePayload, null, 5)); } function generateMainToolInterfaceHtml() { return `
Please enter a query.
').show(); } else { $('#saveQueryForm').show(); $('#saveQueryFormFileName').val(activeSQLFile.fileName || '').focus(); $('#saveQueryFormDescription').val(activeSQLFile.description || ''); } }); $('#workbooksModal').on('shown.bs.modal', function (e) { fetchAndDisplayWorkbooksList(); });`; } function generateJsForSettingDefaultQuery() { return `function setDefaultQueryInEditor() { editor.setValue('SELECT\\n\\tID,\\n\\tLastName,\\n\\tFirstName,\\n\\tPhone,\\n\\tEmail\\nFROM\\n\\tEmployee'); }`; } function generateJsForTogglingPaginationOptions() { return `function togglePaginationOptionsVisibility() { const isChecked = $('#enablePagination').is(':checked'); $('#pagination-options').toggle(isChecked); }`; } function generateJsForRefreshingFileInfoDisplay() { return `function refreshUnsavedFileStatusIndicator() { var content = ''; if (activeSQLFile.source == undefined) { if (editor.getValue() != '') { content = 'Unsaved'; } } else { var status = (editor.getValue() != activeSQLFile.sql) ? 'Changed' : 'Unchanged'; var tooltip = 'Source: ' + activeSQLFile.source + '\\nStatus: ' + status; content = '' + activeSQLFile.fileName + ''; if (status === 'Changed') { content = '' + content + ''; } } $('#fileInfo').html(content); }`; } function generateJsForSubmittingQuery() { return `function submitQueryForExecution() { var queryText = editor.getValue(); if (!queryText) { alert('Please enter a query.'); return; } var selectedText = editor.getSelection(); var theQuery = selectedText || queryText; var rowBegin = 1, rowEnd = 999999; if ($('#enablePagination').is(':checked') && !$('#returnAll').is(':checked')) { rowBegin = parseInt($('#rowBegin').val()) || 1; rowEnd = parseInt($('#rowEnd').val()) || ${rowsReturnedDefault}; } $('#resultsDiv').show(); $('#resultsDiv').html('' + queryResponsePayload.error.message + ''); } else { saveQueryToRecentQueries(theQuery); generateResponseOutputBasedOnFormat(); } }).fail(function(xhr) { $('#resultsDiv').html('
XHR Error: ' + xhr.status + ' ' + xhr.statusText + ''); }); }`; } function generateJsForResizingQueryTextArea() { return `function resizeQueryTextArea() { /* Auto-resize handled by flexbox and CodeMirror */ }`; } function generateJsForGettingRadioFieldValue() { return `function getCheckedRadioValue(name) { return $('input[name="' + name + '"]:checked').val(); }`; } function generateJsForFetchingRemoteLibraryIndex() { return `function fetchAndDisplayRemoteLibraryIndex() { $('#remoteSQLFilesList').html('
Name | Description | |
---|---|---|
' + r.name + ' | ' + r.description + ' |
' + xhr.status + '
'); }); }`; } function generateJsForLoadingRemoteSqlFile() { return `function loadSqlFileFromRemoteLibrary(filename) { $.get('https://suiteql.s3.us-east-1.amazonaws.com/queries/' + filename + '?nonce=' + new Date().getTime()).done(function(response) { editor.setValue(response); activeSQLFile = { source: 'Remote', fileName: filename, sql: response }; refreshUnsavedFileStatusIndicator(); $('#remoteLoadModal').modal('hide'); $('#resultsDiv').hide(); }).fail(function(xhr) { alert('Error: ' + xhr.status); }); }`; } function generateJsForCopyingResponseData() { return `function copyResponseDataToClipboard() { $('#responseData').select(); document.execCommand('copy'); }`; } function generateJsForGeneratingResponseOutput() { return `function generateResponseOutputBasedOnFormat() { switch (getCheckedRadioValue('resultsFormat')) { case 'csv': generateCsvFromResponseData(); break; case 'json': generateJsonFromResponseData(); break; default: generateHtmlTableFromResponseData(); } }`; } function generateJsForGeneratingCsvResponse() { return `function generateCsvFromResponseData() { if (!queryResponsePayload || !queryResponsePayload.records || queryResponsePayload.records.length === 0) { $('#resultsDiv').html('' + col + ' | '; }); table += '
---|
' + val + ' | '; }); table += '
No recent queries found.
'; } else { content = 'Query | |
---|---|
' + displayQuery + ' |
Label | Name | Type |
---|---|---|
' + f.label + ' | ' + f.id + ' | ' + f.dataType + ' |
Table Name |
---|
' + rt.label + ' ' + rt.id + ' |
' + payload.error + '
'); return; } let content = 'Name | Description | |
---|---|---|
' + (r.name || '') + ' | ' + (r.description || '') + ' |
Error: ' + xhr.status + '
'); }); }`; } function generateJsForLoadingLocalSqlFile() { return `function loadSqlFileFromLocalLibraryById(fileID) { $.post({ url: '${scriptURL}', data: JSON.stringify({ 'function': 'sqlFileLoad', 'fileID': fileID }), contentType: 'application/json' }).done(function(payload) { if (payload.error) { alert('Error: ' + payload.error); return; } editor.setValue(payload.sql); activeSQLFile = { source: 'Local', fileName: payload.file.name, description: payload.file.description, fileID: payload.file.id, sql: payload.sql }; refreshUnsavedFileStatusIndicator(); $('#localLoadModal').modal('hide'); $('#resultsDiv').hide(); }).fail(function(xhr) { alert('Error: ' + xhr.status); }); }`; } function generateJsForSavingLocalSqlFile() { return `function saveSqlFileToLocalLibraryWithPrompt() { var filename = $('#saveQueryFormFileName').val(); if (!filename) { alert('Please enter a file name.'); return; } $.post({ url: '${scriptURL}', data: JSON.stringify({ 'function': 'sqlFileExists', 'filename': filename }), contentType: 'application/json' }).done(function(fileExistsPayload) { if (fileExistsPayload.exists && !confirm("A file named '" + filename + "' already exists. Overwrite?")) { return; } var savePayload = { 'function': 'sqlFileSave', 'filename': filename, 'contents': editor.getValue(), 'description': $('#saveQueryFormDescription').val() }; $.post({ url: '${scriptURL}', data: JSON.stringify(savePayload), contentType: 'application/json' }).done(function(fileSavePayload) { if (fileSavePayload.error) { alert('Error: ' + fileSavePayload.error); return; } activeSQLFile = { source: 'Local', fileName: filename, description: savePayload.description, fileID: fileSavePayload.fileID, sql: savePayload.contents }; refreshUnsavedFileStatusIndicator(); alert('File saved.'); $('#saveModal').modal('hide'); }).fail(function(xhr) { alert('Save Error: ' + xhr.status); }); }).fail(function(xhr) { alert('File Check Error: ' + xhr.status); }); }`; } function generateJsForLoadingWorkbook() { return `function loadQueryFromWorkbookById(scriptID) { $.post({ url: '${scriptURL}', data: JSON.stringify({ 'function': 'workbookLoad', 'scriptID': scriptID }), contentType: 'application/json' }).done(function(payload) { if (payload.error) { alert('Error: ' + payload.error); return; } editor.setValue(payload.sql); $('#workbooksModal').modal('hide'); $('#resultsDiv').hide(); activeSQLFile = { source: 'Workbook ' + scriptID, fileName: '', description: '', fileID: '', sql: payload.sql }; refreshUnsavedFileStatusIndicator(); }).fail(function(xhr) { alert('Error: ' + xhr.status); }); }`; } function generateJsForGettingWorkbooksList() { return `function fetchAndDisplayWorkbooksList() { $('#workbooksList').html('' + payload.error + '
'); return; } let content = 'Name | Description | Owner | |
---|---|---|---|
' + (r.name || '') + ' | ' + (r.description || '') + ' | ' + (r.owner || '') + ' |
Error loading workbooks: ' + xhr.statusText + '
'); }); }`; }