Trying to run a Suitelet script to call a search and transform into an invoice. But before going into the next step I have encountered the message below:
Fail to evaluate script: All SuiteScript API Modules are unavailable while executing your define callback.
My script:
/**
* #NApiVersion 2.x
* #NScriptType Suitelet
*/
require(['N/search','N/log'], function (search,log) {
function onRequest(context) {
// Search
// ### ORDERS TO BILL
var salesorderSearchObj = search.create({
type: "salesorder",
filters:
[
["type","anyof","SalesOrd"],
"AND",
["status","anyof","SalesOrd:E","SalesOrd:F"],
"AND",
["mainline","is","T"]
],
columns:
[
search.createColumn({name: "amount", label: "Amount"}),
search.createColumn({name: "entity", label: "Name"}),
search.createColumn({name: "account", label: "Account"}),
search.createColumn({
name: "datecreated",
sort: search.Sort.DESC,
label: "Date Created"
}),
search.createColumn({name: "billeddate", label: "Date Billed"}),
search.createColumn({name: "statusref", label: "Status"})
]
});
var searchResultCount = salesorderSearchObj.runPaged().count;
log.debug( {"title":"salesorderSearchObj result count", "details":searchResultCount});
salesorderSearchObj.run().each(function(result){
// .run().each has a limit of 4,000 results
return true;
});
}
return {
onRequest: onRequest
};
});
It is to map and reduce selected sales orders.
Load search.
Map by transforming selected orders into one (by customer).
Reduce into one invoice/cash sale.
AS bknights suggested, script samples provided by NetSuite are just part of a script and they are not complete scripts. So before using them, you need to make sure your script is valid and complete.
As for the error message Fail to evaluate script, all of your script(which uses NetSuite modules) needs to be in a function inside the callback. i.e Suitelet has one entryPoint onRequest so shifting all of your script (in callback) under it should work.
example:
/**
* #NApiVersion 2.x
* #NScriptType Suitelet
*/
define(['N/search'], function (search) {
function onRequest(context) {
// Your search and order transform code should be here
// ### ORDERS TO BILL
}
return {
onRequest: onRequest
};
});
For further reading check this out.
Related
I am trying to write a UserEvent script which prompts the user for a confirmation when they attempt to edit a sales order which has already had it's picking ticket printed. Below is my code:
define(['N/record', 'N/search', 'N/log', 'N/runtime', 'N/ui/dialog'], function (record, search, log, runtime, dialog) {
/**
*#NApiVersion 2.1
*#NScriptType UserEventScript
*/
var result = true;
function beforeSubmit(context) {
var order = context.oldRecord;
var orderStatus = order.getValue({fieldId: "status"});
if(orderStatus != "Billed") {
var orderInternalID = order.getValue({fieldId: "id"});
log.debug("id", orderInternalID);
var systemnoteSearchObj = search.create({
type: "systemnote",
filters:
[
["recordid","equalto",String(orderInternalID)],
"AND",
["field","anyof","TRANDOC.BPRINTEDPICKINGTICKET"],
"AND",
["newvalue","is","T"]
],
columns:
[
search.createColumn({
name: "record",
sort: search.Sort.ASC,
label: "Record"
}),
search.createColumn({name: "name", label: "Set by"}),
search.createColumn({name: "date", label: "Date"}),
search.createColumn({name: "context", label: "Context"}),
search.createColumn({name: "type", label: "Type"}),
search.createColumn({name: "field", label: "Field"}),
search.createColumn({name: "oldvalue", label: "Old Value"}),
search.createColumn({name: "newvalue", label: "New Value"}),
search.createColumn({name: "role", label: "Role"})
]
});
var searchResultCount = systemnoteSearchObj.runPaged().count;
log.debug("systemnoteSearchObj result count",Number(searchResultCount));
if(Number(searchResultCount) > 0) {
var options = {
title: 'WARNING: Pick Ticket Printed',
message: 'The pick ticket has already been printed for this sales order. Are you sure you want to edit?'
};
dialog.confirm(options).then(confirm).catch(cancel);
}
}
log.debug("result", result);
return result;
}
function confirm(reason) {
log.debug("User confirmed save.", reason);
result = true;
return true;
}
function cancel(reason) {
log.debug("User cancelled save.", reason);
result = false;
return false;
}
return {
beforeSubmit: beforeSubmit
}
});
When I deploy this script on the Edit event and try to edit and save the order, I get no confirmation dialog and instead receive the following error:
TypeError: dialog.confirm(...).then is not a function [at Object.beforeSubmit. From what I've seen, this type of error occurs when you attempt to use .then() on a function which does not return a promise. However, in the documentation for dialog.confirm(), it clearly states that the function does in fact return a promise.
Why am I receiving this error and how can I avoid it to achieve the goal of the script?
A User Event script runs on the server side and thus cannot render UI components (there's no browser instance or window object to render on). You'll likely need to transition this logic to the saveRecord event of a Client Script.
I`m creating bot for MS Teams and using JS Microsoft Bot Framework V4 SDK.
In my work, I use search message extension and to work with it, I implemented the onSelectItem method that returns a adaptive card. I will give an example of the code below.
return Promise.resolve({
type: "result",
attachmentLayout: "list",
attachments: [CardFactory.heroCard(
`${file.name}`,
`${text}`,
undefined,
CardFactory.actions([
{
type: "openUrl",
title: "Open",
value: `${openLink}`
},
{
type: "openUrl",
title: "Download",
value: `${downloadLink}`
},
]),
)]
});
Where I pass undefined, this should be the path to the picture, but in my implementation I don't need it. So and this code works great in the browser and on the desktop version here is a screenshot
however, on the mobile version, I get the following result
this is absolutely not the right card, it has no content or buttons
I think I found the answer myself. When the message extension search is triggered, the onQuery method is called and suppose you made a query and received an array of values that you want to display. And here, in the same method, iterating over the array, you must draw two cards at once. For example
files.forEach((file: IDocumentInfo): void => {
const card: any = CardFactory.heroCard(
cutString(file.name, LIMIT),
text,
undefined,
[
{
type: "openUrl",
title: "Open",
value: "", // some value
},
{
type: "openUrl",
title: "Download",
value: "" //some value,
},
]
);
const preview: any = {
contentType: "application/vnd.microsoft.card.thumbnail",
content: {
title: `${cutString(file.name, LIMIT)}`,
text: "", // some text
}
};
And here the variable preview in my case will respond to a small view of information after the search and the variable card will be responsible for the view after selection. And after the card is selected, the onSelectItem method is triggered, which I need to get more information about the document
It turns out that the adaptive heroCard is not to blame here, the onSelectItem method is not called in the mobile application, or I am doing something wrong
We are using Backgrid which allows you to define grid columns with an array of Javascript objects which it converts to a collection. We are trying to take advantage of this to have configurable validation on a column by column basis, so we might have the following where we've added a "validator" function to a couple of the columns:
[
{
label: "Delete",
name: "delete",
cell: "boolean"
},
{
label: "Alias",
name: "alias",
cell: "string",
sortType: "toggle",
validator: function (value) {
return true;
}
},
{
label: "Assigned Server",
name: "assignedServer",
cell: "string",
sortType: "toggle",
validator: function (value) {
return new Error("Cannot Assign Server")
}
}
]
We are listening to edits to the grid in the following prescribed manner and for the purposes of this question we can ignore the model argument to the function but concentrate on the column (delete, alias or assignedServer from above) which is itself a model in a collection. So far I have a snippet of code leveraging underscore.js's _.filter that returns the validatableColumns but I want to take this further and end up with an object of the format {name: validator, etc...}. Bearing in mind my specific use case, what is a succinct way to create an object from a Backbone collection that maps model values to one another?
certificateGrid.listenTo(certificateCollection, "backgrid:edited", function (model, column) {
var validatableColumns = _.filter(column.collection.models, function (c) {
return c.get('validator');
});
//etc.
Using _.reduce seems to do the trick:
var validatorFns = _.reduce(column.collection.models, function (fns, model) {
var validator = model.get('validator');
if (model.get('validator')) {
fns[model.get('name')] = validator;
}
return fns;
}, {});
I have an OnDemandGrid with one column that I want to populate with a custom Dojo widget I built. The data used to populate each of these widgets comes from a Solr query. Since I am expecting possibly thousands of search results, I need to use a JsonRest object to make the queries and handle pagination. Here's what I have so far:
The store:
var store = new JsonRest ({
target: "/solr/json/response",
});
Creating the grid:
var grid = new (declare([OnDemandGrid, Pagination])) ({
store: store,
getBeforePut: false,
columns: [
{
label: "Test",
field: "first",
renderCell: myRenderFunction //To render the custom widget
}
]
}, "grid");
grid.startup();
myRenderFunction:
var myRenderFunction = function(object, data, cell) {
var widget = new MyCustomWidget({
doc: object,
foo: bar
}, cell.appendChild(document.createElement("div"));
widget.startup();
return widget;
}
Sample Solr response, in JSON form:
{
"response":{
"docs":[
{
"foo": "Hello",
"bar": "World"
},
{
"foo": "Easy as",
"bar": "ABC"
},
{
"foo": "Simple as",
"bar": "Do re mi"
}
]
},
"highlighting": { ... },
"numFound": "74",
"start": 0
}
I have followed a few examples online demonstrating how to do this using JsonRest and any of the dgrid flavors (and they all worked), but when I try to render the widget to the grid nothing shows up and I get a TypeError: transform(...) is null.
Is there any reason why I can't render my widget to the grid?
I ran into the same problem trying to use Solr results with dgrid and JsonRest.
JsonRest uses QueryResults as a wrapper for what it returns.
Your problem is that QueryResults accepts only arrays or promises and you're currently giving it an object.
In order to give QueryResults the docs array, write a custom JsonRest store similar to:
define([
"dojo/Deferred", "dojo/io-query", "dojo/_base/declare", "dojo/request/xhr",
"dojo/store/JsonRest", "dojo/store/util/QueryResults"
], function (Deferred, ioQuery, declare, xhr, JsonRest, QueryResults) {
return declare([JsonRest], {
target: '/solr/json/response',
idProperty: 'foo',
query: function (query, options) {
var results, total, count = options.count, start = options.start;
if (start > 0 || count >= 0) {
query.start = start;
query.rows = ((options.hasOwnProperty('count') &&
count !== Infinity) ? count : 25);
} else {
console.error('Missing start and count arguments');
return;
}
results = new Deferred();
results.total = new Deferred();
xhr(this.target, {
query: ioQuery.objectToQuery(query),
handleAs: 'json',
headers: {
Accept: this.accepts
}
}).then(function (data) {
total = data.response.numFound;
results.total.resolve(total);
results.resolve(data.response.docs);
}, function (e) {
console.error(e.response.status + '. ' + e.message);
});
return new QueryResults(results);
}
});
});
I also recommend waiting to use a custom renderCell function until after you get dgrid properly populated.
edit: OnDemandGrid is not going to work with the Pagination extension.
So decide whether you want discreet paging controls or infinite scroll (paging handled by dgrid).
See Pagination and OnDemand docs.
I have two grids in my application.
var columns1 = [
{
name: "Address",
field: "address"
id: "address",
sortable: true
}
]
var columns2 = [
{
{
name: "Rating, in %",
field: "rating"
id: "rating_percent",
resizable: false
}
]
They are absolutely independent from each other. Also, I have some grid events descriptions in another js file.
grid.onColumnsReordered.subscribe(function (e, args) {
_this.updateHeaderRow();
// here
});
When user changes the columns order, then I want to save this order. Should I change (overwrite) the DOM elements, I mean column1 and column2?
So question: how can I save the columns order?
njr101's answer (using store.js) is great, but note: store.js cannot store functions (i.e. for editors, formatters), so once you store.get() the columns, you'll need to add the functions back, using your original stock "columns" array:
if (store.get('gridColumns')) {
grid.setColumns(store.get('gridColumns')); // Restore settings if available
grid.getColumns().forEach(function(ch) { // Re-create editor and formatter functions
var result = $.grep(columns, function(e){ return e.id == ch.id; });
if (result[0]) {
ch.editor = result[0].editor;
ch.formatter = result[0].formatter;
}
});
}
I have done this before and the easiest way I found was to store the columns in local storage. I use the store.js library which makes this pretty simple.
grid.onColumnsReordered.subscribe(function (e, args) {
store.set('gridColumns', grid.getColumns());
});
When you want to restore the columns (e.g. when the user returns to the page) you can just call:
grid.setColumns(store.get('gridColumns'));