Suitescript 2.0 setting coupons and partner codes - rest

We created a suitescript 2.0 script in our netsuite environment. We are using RESTlet to access it.
Our script creates a sales order with various fields. It works fine but we are unable to set a coupon code value or a partner code, we get the same error for both. We are using Internal ID and we tried coupon code itself as well.
Any idea?
Error:
{
"type":"error.SuiteScriptError",
"name":"INVALID_FLD_VALUE",
"message":"You have entered an Invalid Field Value 18 for the following field: couponcode",
"stack":[
"<anonymous>(N/record/recordService.js)",
"setSalesOrderData(adhoc$-1$debugger.user:71)",
"saveSaleOrder(adhoc$-1$debugger.user:17)",
"<anonymous>(adhoc$-1$debugger.user:107)",
"<anonymous>(adhoc$-1$debugger.user:6)"
],
"cause":{
"type":"internal error",
"code":"INVALID_FLD_VALUE",
"details":"You have entered an Invalid Field Value 18 for the following field: couponcode",
"userEvent":null,
"stackTrace":[
"<anonymous>(N/record/recordService.js)",
"setSalesOrderData(adhoc$-1$debugger.user:71)",
"saveSaleOrder(adhoc$-1$debugger.user:17)",
"<anonymous>(adhoc$-1$debugger.user:107)",
"<anonymous>(adhoc$-1$debugger.user:6)"
],
"notifyOff":false},"id":"","notifyOff":false
}
}
RESTlet code:
var objRecord = record.create({
type: record.Type.SALES_ORDER,
isDynamic: true
});
/* add other values.....*/
objRecord.setValue({ fieldId: 'couponcode', value: 538 });
var recordId = objRecord.save({
enableSourcing: false,
ignoreMandatoryFields: false
});

Are these coupon codes you are trying to set One-Time Use codes? Or are they linked to a Promotion?
Which internal ID are you using in the couponcode field?
Can you share the relevant parts of your RESTlet code as well?
I tested the following in the console (i.e. a Client Script) on a Sales Order, and it seems to set a Promotion and Coupon Code appropriately:
require(["N/currentRecord"], function(c) {
c.get().setValue({
"fieldId": "couponcode",
"value": 1
});
});
where 1 is the internal ID of the Promotion. If I use an internal ID not associated to a Promotion, I get no error, but nothing is populated in either field.

we finally got working code from Netsuite support, since there is such little help on this topic online, I am sharing it here. We grabbed what we needed into our own script, but this basic one works as well,
From netsuite support agent:
I created a simple SuiteScript 2.0 code for entering the values into Partner field (id: 'partner') and Coupon Code (id: 'couponcode'). Both fields are dropdown fields not multiselect fields.
The field Coupon Code depends on Promotion field that's why we should enter value in 'promocode' field instead of 'couponcode'.
/**
*#NApiVersion 2.x
*#NScriptType usereventscript
*/
define(['N/record'],
function(record) {
function AfterSubmit(context) {
var result = record.load({
type: 'salesorder',
id: 71040,
isDynamic: true
});
result.setValue ({
fieldId : 'partner',
value : 45140
});
result.setValue ({
fieldId : 'couponcode',
value : 'AMARILLO16'
});
result.save({
enableSourcing : false,
ignoreMandatoryFields : true
});
return true;
}
return {
afterSubmit: AfterSubmit
};
});
We had to do one modification for it to work for us:
result.setValue ({
fieldId : 'partner',
value : 45140
});
result.setText ({
fieldId : 'couponcode',
text : 'AMARILLO16'
});
result.save({
enableSourcing : false,
ignoreMandatoryFields : true
});

Related

Trying to store SuiteScript search result as a lookup parameter

I have the following code which returns the internal ID of a sales order by looking it up from a support case record.
So the order of events is:
A support case is received via email
The free text message body field contains a reference to a sales order transaction number. This is identified by the use of the number convention of 'SO1547878'
A workflow is triggered on case creation from the email case creation feature. The sales order number is extracted and stored in a custom field.
The internal ID of the record is looked up and written to the console (log debug) using the workflow action script below:
*#NApiVersion 2.x
*#NScriptType WorkflowActionScript
* #param {Object} context
define(["N/search", "N/record"], function (search, record) {
function onAction(context) {
var recordObj = context.newRecord;
var oc_number = recordObj.getValue({ fieldId: "custevent_case_creation" });
var s = search
.create({
type: "salesorder",
filters: [
search.createFilter({
name: "tranid",
operator: search.Operator.IS,
values: [oc_number],
}),
],
columns: ["internalid"],
})
.run()
.getRange({
start: 0,
end: 1,
});
log.debug("result set", s);
return s[0];
}
return {
onAction: onAction,
};
});
I am trying to return the resulting internal ID as a parameter so I can create a link to the record on the case record.
I'm getting stuck trying to work out how I would do this?
Is there a way to store the value on the case record, of the internal ID, that is looked up? (i.e.the one currently on the debug logs)?
I am very new to JS and Suitescript so am not sure at what point in this process, this value would need to be stored in the support case record.
At the moment. the workflow action script (which is the part of the workflow the above script relates to) is set to trigger after submit
Thanks
Edit: Thanks to Bknights, I have a solution that works.
The workflow:
The new revised script is as follows:
*#NApiVersion 2.x
*#NScriptType WorkflowActionScript
* #param {Object} context
*/
define(["N/search", "N/record"], function (search, record) {
function onAction(context) {
var recordObj = context.newRecord;
var oc_number = recordObj.getValue({ fieldId: "custevent_case_creation" });
var s = search
.create({
type: "salesorder",
filters: [
search.createFilter({
name: "tranid",
operator: search.Operator.IS,
values: [oc_number],
}),
],
columns: ["internalid"],
})
.run()
.getRange({
start: 0,
end: 1,
});
log.debug("result set", s[0].id);
return s[0].id;
}
return {
onAction: onAction,
};
});
On the script record for the workflow action script, set the type of return you expect. In this case, it would be a sales order record:
This would allow you to use a list/record field to store the value from the 'search message' workflow action created by the script
the result
Edit 2: A variation of this
/**
*#NApiVersion 2.x
*#NScriptType WorkflowActionScript
* #param {Object} context
*/
define(["N/search", "N/record"], function (search, record) {
function onAction(context) {
try {
var recordObj = context.newRecord;
var oc_number = recordObj.getValue({
fieldId: "custevent_case_creation",
});
var s = search
.create({
type: "salesorder",
filters: [
search.createFilter({
name: "tranid",
operator: search.Operator.IS,
values: [oc_number],
}),
],
columns: ["internalid","department"],
})
.run()
.getRange({
start: 0,
end: 1,
});
log.debug("result set", s[0]);
recordObj.setValue({fieldId:'custevent_case_sales_order', value:s[0].id});
// return s[0]
} catch (error) {
log.debug(
error.name,
"recordObjId: " +
recordObj.id +
", oc_number:" +
oc_number +
", message: " +
error.message
);
}
}
return {
onAction: onAction,
};
});
Depending on what you want to do with the order link you can do a couple of things.
If you want to reference the Sales Order record from the Support Case record you'd want to add a custom List/Record field to support cases that references transactions. (ex custevent_case_order)
Then move this script to a beforeSubmit UserEvent script and instead of returning extend it like:
recordObj.setValue({fieldId:'custevent_case_order', value:s[0].id});
For performance you'll probably want to test whether you are in a create/update event and that the custom order field is not yet filled in.
If this is part of a larger workflow you may still want to look up the Sales Order in the user event script and then start you workflow when that field has been populated.
If you want to keep the workflow intact your current code could return s[0].id to a workflow or workflow action custom field and then apply it to the case with a Set Field Value action.

LoopBack4 MongoDB Auto Increment custom ID

LoopBack itself is new for me and I see version 4 is way too different from version 3. My requirement is that I need to have a custom auto incremented id in my mongoDB document every time I create a POST to the REST end point similar to a running id in a MySQL database.
I did check this (auto-increment using loopback.js and MongoDB) and (https://gist.github.com/drmikecrowe/5a5568930bad567d4148aad75c94de5a) with a version 3 setup, but i did not find proper document to replicate the same on version 4.
Currently I am using a basic app with the out of the box REST implementations provided from the loopback 4. Below is an example of my model.
export class Test extends Entity {
#property({
type: 'string',
id: true,
})
_id?: string;
#property({
type: 'number',
generated: true,
required: false
})
id: number;
#property({
type: 'string',
required: true,
})
name: string;
#property({
type: 'boolean',
required: true,
})
val: boolean;
constructor(data?: Partial<Test>) {
super(data);
}
}
My mongodb document should look something like this:
{
"_id" : ObjectId("5c373c1168d18c18c4382e00"),
"id" : 1
"name" : "aaaa",
"val" : true
}
{
"_id" : ObjectId("5c3869a55548141c0c27f298"),
"id" : 2
"name" : "bbbbb",
"val" : false
}
You can do something like in this example
#post('/characters', {
responses: {
'200': {
description: 'Character model instance',
content: {'application/json': {schema: {'x-ts-type': Character}}},
},
},
})
async create(#requestBody() character: Character): Promise<Character> {
//add following lines
let characterId = 1;
while(await this.characterRepository.exists(characterId)){
characterId ++;
}
character.id = characterId;
//add above lines
return await this.characterRepository.create(character);
}
you probably already noticed the auto-increment id feature. When you call the post API multiple times (leave id blank), the id increased by 1 every time. This feature is supported by the in-memory database. But we are using MongoDB in this project. If we want to have that feature, we need to do that programmatically.
For more information follow below link
https://strongloop.com/strongblog/building-online-game-with-loopback-4-pt1/
see the section just above the API Explorer heading
or find for 'auto increment id' you will be taken to that paragraph
Hopefully, this helps, write me if there is any other query.
Thanks
I'm also playing with Mongo and it can autogenerate your id for you.
Specifically, when you create your model, using lb4 model, choosing 'Entity' and then you're prompted:
Let's add a property to Participant
Enter an empty property name when done
? Enter the property name: id
? Property type: string
? Is id the ID property? Yes
? Is id generated automatically? Yes
This will generate your model with the property:
#property({
type: 'string',
id: true,
generated: true,
})
id?: string;
Great.. then when creating your CRUD controller:
? What kind of controller would you like to generate? REST Controller with CRUD functions
? What is the name of the model to use with this CRUD repository? Person
? What is the name of your CRUD repository? PersonRepository
? What is the name of ID property? id
? What is the type of your ID? string
? Is the id omitted when creating a new instance? Yes
? What is the base HTTP path name of the CRUD operations? /persons
Now when hitting your endpoint, the create POST doesn't take an ID, but will return one for you.
You can do something like in this example
let last_record = await this.testRepository.findOne({order: ['id DESC']});
if(last_record) invoice.id = last_record.id+1;
This will generate your model with the property:
#property({
type: 'number',
id: true,
default: 1,
generated: false
})
id: number;
Hopefully, this helps, please write me if there is any other code. Thanks
This class inherits from the DefaultCrudRepository class and overrides the create method. The method uses the "Counters" collection to hold the last id of the current data class (this.entityClass.name). The findAndModify method will prevent duplicate id values from being created.
import {DefaultCrudRepository, Entity, DataObject, Options} from '#loopback/repository';
export class MongoAutoIncIdRepository<T extends Entity, ID, Relations extends object = {}> extends DefaultCrudRepository<T, ID, Relations> {
public async create(entity: DataObject<T>, options?: Options): Promise<T> {
if (!this.dataSource.connected) {
await this.dataSource.connect()
}
let mongoConnector = this.dataSource.connector!
let collection = mongoConnector.db.collection('Counters')
let result = await collection.findAndModify(
{
collection: this.entityClass.name
},
[['_id', 'asc']],
{
$inc: {value: 1}
},
{
upsert: true,
new: true
})
console.log(result)
// #ts-ignore
entity.id = result.value.value
return super.create(entity, options)
}
}
It's easy to use. Inherit your repository not from DefaultCrudRepository, but from MongoAutoIncIdRepository if auto increment is required. Then, when the create method is called, the id will increase by 1 automatically.

How to Validate /check the input fields value exist in back end DB or not during live change in sapui5

I have requirement where in need to create the record from SAPui5 application,
For that we have Form and enterthe all details and submit to the data base.
Now i need to validate the first field value, if that value exist in the system/DB need to populate the error, like this record already exist during livechange.
For E.g., Input fields are as follows.
Empld : 121
EmpName : tom
On Change of Empid value need to check 121 record exist in the database or not.
Following are the blogs refereed for the solution but didn't get the solution for the same.
https://blogs.sap.com/2015/10/19/how-to-sapui5-user-input-validations/
https://blogs.sap.com/2015/11/01/generic-sapui5-form-validator/
As i"m new to SAPUI5.Please help me with the coding.
Thanks in advance.
I don't know how much you are aware of Requests to the Backend but maybe you could make a Read Operation and check if there is any data returned:
First solution could be like this (with Entity key):
this.getOwnerComponent().getModel().read("/EntityPath", {
success: function(oData, response) {
if(oData.results.length === 0) {
console.log("Nothing found for this key");
}
},
error: function(oError) {
//Error Handling here
}
});
Or you could build a Filter, pass it to the read operation and check if there is any data returned:
var aFilter = new sap.m.Filter(new Filter("EmpId", sap.m.FilterOperator.EQ, "value"));
this.getOwnerComponent().getModel().read("/EntitySet", {
filters: aFilter,
success: function(oData, response) {
if(oData.results.length === 0) {
console.log("User is not available");
}
},
error: function(oError) {
//Error Handling here
}
});
However, this isn't the best way to check if there is already an entry in your database. You should do this in your Business Logic with Error Messages which get passed to the Frontend.
Hope this helps :-)

Passing discount code in Orders API on Shopify

I have been trying to develop an app that takes an order on Shopify on a different channel. I successfully placed an order through the API but I am not able to include the discount code along with the order. The JSON object for the POST data is as below:
{
order: {
email : request.params.order.email, // string
financial_status : 'pending', // string
send_receipt : true, // boolean
send_fulfillment_receipt : false, // boolean
note : request.params.order.note, // string
discount_codes : [], // supposed to be an array of Object| Problem here,
line_items : request.params.order.line_items, // array
customer : request.params.customer, // JSON object
billing_address : request.params.order.billing_address, // JSON object
shipping_address : request.params.order.shipping_address // JSON object
}
}
According to the documentation, the discount_codes is like this -
Applicable discount codes that can be applied to the order. If no codes exist the value will default to blank. A Discount code will include the following fields:
amount: The amount of the discount.
code: The discount code.
type: The type of discount. Can be one of : "percentage", "shipping", "fixed_amount" (default).
What am I doing wrong? My discount_codes is this
[{amount: 100,code:'WELCOME10',type:'percentage'}]
Has anyone done this before?
According to this response from Shopify what you are trying to do is only possible if you pass the total_discounts field along as well with the total amount of the discount you want to apply.
As you will see in this other answer, any codes you have created through Shopify are not available to use with the API and their usage will not be recorded.
I was trying to use this API in order to test the application of different coupon codes that I was generating, but this does not seem to be possible. Apparently, the API was intended for applying discounts that are custom, not ones that already exist in Shopify. This is a frustrating limitation to me.
I successfully create orders with discounts all the time, without ShopifyPlus as that is irrelevant. The data structure that works for me looks like this:
[ { "code": "Shop By PizzleFuzzle 10%", amount: "10", "type": "percentage" } ]
The discount object is available only for Shopify Plus merchants.
Once you are a Shopify Plus merchant, you will be able to create discount codes like that:
POST /admin/discounts.json
{
"discount": {
"discount_type": "percentage",
"value": "15.0",
"code": "balderdash"
}
}
Please see more detailed documentation in the discount object at Shopify API: https://help.shopify.com/api/reference/discount
You should use the value property name instead of amount property name.
e.g.
{value: 100,code:'WELCOME10',type:'percentage'}
and not
{amount: 100,code:'WELCOME10',type:'percentage'}

Sort populated record in sails waterline

I created a Sails application with two models Publication and Worksheet. They are having a one-to-one relationship. Sails-postgresql is the adapter I'm using. I'm using waterline orm to fire query to the database. I'm When I am trying to load publications data along with worksheet and then sort the records depending on a field in the Worksheet using sort() I'm getting an error.
My model is:
Publication.js
module.exports = {
attributes: {
id: {
type: 'integer'
unique: true
},
worksheetId: {
type: 'integer',
model : 'worksheet'
},
status: {
type: 'string',
defaultsTo: 'active',
in : ['active', 'disabled'],
}
}
}
Worksheet.js
module.exports = {
attributes: {
id: {
type: 'integer',
unique: true
},
name: 'string',
orderWeight: {
type: 'integer',
defaultsTo: 0
}
}
}
So now I want to load all the publication where status is "active" and populate worksheet in the data.
So I'm executing the query:
Publication.find({
where: {
status: 'active'
}
})
.populate('worksheetId').limit(1)
.exec(function (error, publications) {
})
And I'm getting a data like :
{
id : 1,
status : "active",
worksheetId : {
id : 1
name : "test",
orderWeight : 10
}
}
So till now it's all working fine. Now I want to increase the limit to 10 and want to sort the data depending on "orderWeight" which is in the populated data. Initially I sorted the whole data depending on publication id and the query worked.
Publication.find({
where: {
status: 'active'
}
})
.populate('worksheetId').sort('id ASC').limit(10)
.exec(function (error, publications) {
})
So I fired similar query to sort the data on "orderWeight"
Publication.find({
where: {
status: 'active'
}
})
.populate('worksheetId').sort('worksheetId.orderWeight ASC').limit(10)
.exec(function (error, publications) {
})
And this query is giving me error that worksheetId.orderWeight is not a column on the publication table. So I want to fire this sort query on the populated data not on the publication table.
Please let me know how I can get my expected result.
Apart from sort() method I also want to run some find command to the populated data to get those publication where the worksheet name matches with certain key as well.
Basically, what you're trying to do, is query an association's attribute. This has been in the waterline roadmap since 2014, but it's still not supported, so you'll have to figure out a workaround.
One option is to query the Worksheet model, and populate the Publication, since sails doesn't let you query across models without using raw queries (i.e. .sort('worksheetId.orderWeight ASC') doesn't work). Unfortunately, you might have to move the active flag to the Worksheet. For example:
Worksheet.find({
status: 'active'
})
.populate('publication') // you should also add publication to Worksheet.js
.sort('orderWeight ASC')
.limit(10)
Alternatively, you could combine Worksheet and Publication into one model, since they're one-to-one. Probably not ideal, but sails.js and Waterline make it very difficult to work with relational data - I'd estimate that half of the queries in the project I'm working on are raw queries due to sails' poor support of postgres. The framework is pretty biased towards using MongoDB, although it claims to "just work" with any of the "supported" DBs.