add button and update sales order using netsuite - button

I am new to netsuite,i need to update sales order by adding an item to it using a button click i had write the code for button using user event script and code for updating sales order using client script.but my client script is not working
/**
* #NApiVersion 2.x
* #NScriptType ClientScript
* #NModuleScope SameAccount
*/
define(['N/currentRecord','N/record'],
function(record) {
function buttonclick() {
try{
var CurrRecord = currentRecord.get();
var recIdSO= CurrRecord.id;
var salesOrder = record.load({
type: record.Type.SALES_ORDER,
id:recIdSO ,
isDynamic: true
});
log.debug({
title: 'recordid',
details: 'Id: ' + recIdSO
});
var line=salesOrder.selectNewLine({
sublistId: 'item'
});
salesOrder.setCurrentSublistValue({
sublistId : 'item',
fieldId : 'item',
value :510 ,
ignoreFieldChange: true
});
salesOrder.setCurrentSublistValue({
sublistId : 'item',
fieldId : 'amount',
value :100 ,
ignoreFieldChange: true
});
salesOrder.commitLine({
sublistId: 'item'
});
var recId = salesOrder.save();
log.debug({
title: 'Record updated successfully',
details: 'Id: ' + recId
});
}catch (e) {
log.error({
title: e.name,
details: e.message
});
}
}
/**
* Function to be executed after page is initialized.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #param {string} scriptContext.mode - The mode in which the record is being accessed (create, copy, or edit)
*
* #since 2015.2
*
function pageInit(scriptContext) {
}
/**
* Function to be executed when field is changed.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #param {string} scriptContext.sublistId - Sublist name
* #param {string} scriptContext.fieldId - Field name
* #param {number} scriptContext.lineNum - Line number. Will be undefined if not a sublist or matrix field
* #param {number} scriptContext.columnNum - Line number. Will be undefined if not a matrix field
*
* #since 2015.2
*
function fieldChanged(scriptContext) {
}
/**
* Function to be executed when field is slaved.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #param {string} scriptContext.sublistId - Sublist name
* #param {string} scriptContext.fieldId - Field name
*
* #since 2015.2
*
function postSourcing(scriptContext) {
}
/**
* Function to be executed after sublist is inserted, removed, or edited.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #param {string} scriptContext.sublistId - Sublist name
*
* #since 2015.2
*
function sublistChanged(scriptContext) {
}
/**
* Function to be executed after line is selected.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #param {string} scriptContext.sublistId - Sublist name
*
* #since 2015.2
*
function lineInit(scriptContext) {
}
/**
* Validation function to be executed when field is changed.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #param {string} scriptContext.sublistId - Sublist name
* #param {string} scriptContext.fieldId - Field name
* #param {number} scriptContext.lineNum - Line number. Will be undefined if not a sublist or matrix field
* #param {number} scriptContext.columnNum - Line number. Will be undefined if not a matrix field
*
* #returns {boolean} Return true if field is valid
*
* #since 2015.2
*
function validateField(scriptContext) {
}
/**
* Validation function to be executed when sublist line is committed.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #param {string} scriptContext.sublistId - Sublist name
*
* #returns {boolean} Return true if sublist line is valid
*
* #since 2015.2
*
function validateLine(scriptContext) {
}
/**
* Validation function to be executed when sublist line is inserted.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #param {string} scriptContext.sublistId - Sublist name
*
* #returns {boolean} Return true if sublist line is valid
*
* #since 2015.2
*
function validateInsert(scriptContext) {
}
/**
* Validation function to be executed when record is deleted.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #param {string} scriptContext.sublistId - Sublist name
*
* #returns {boolean} Return true if sublist line is valid
*
* #since 2015.2
*
function validateDelete(scriptContext) {
}
/**
* Validation function to be executed when record is saved.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #returns {boolean} Return true if record is valid
*
* #since 2015.2
*/
function saveRecord(scriptContext) {
}
return {
saveRecord: saveRecord,
buttonclick:buttonclick
};
});

Your current example is almost correct.
You need a couple of things:
first add fireSlavingSync:true as a property for all your setCurrentLineItemValue methods. Otherwise Netsuite might not have finished updating the item's field by the time you get to your commitLine call.
I'd also tend to set ignoreFieldChange:false.
you probably need to set a price level on the line item. Depending on your version of Netsuite you should be able to just set a custom price level:
salesOrder.setCurrentSublistValue({
sublistId : 'item',
fieldId : 'price',
value :-1 ,
ignoreFieldChange: false,
fireSlavingSync:true
});

AFAIK, if you add buttons from client script, they are only available in EDIT mode and you are trying to load same record and update it, this would result in record has been changed error message on UI(Also, NetSuite doesn't recommend this). The better way would be to use workflows to update records.
In workflow you could add button using the workflow itself and on button click fire your workflow action script to update line item on the Order.
Here is sample you can refer to.

/**
* #NApiVersion 2.x
* #NScriptType ClientScript
* #NModuleScope SameAccount
*/
define(['N/currentRecord', 'N/record'],
function (currentRecord, record) {
function pageInit(scriptContext) {
}
function buttonclick() {
try {
var salesOrder = currentRecord.get();
console.log(salesOrder)
var recIdSO = salesOrder.id;
/* var salesOrder = record.load({
type: record.Type.SALES_ORDER,
id:recIdSO ,
isDynamic: true
});*/
/* console.log('recordid'+ recIdSO);
*/ /*log.debug({
title: 'recordid',
details: 'Id: ' + recIdSO
}); */
var line = salesOrder.selectNewLine({
sublistId: 'item'
});
console.log('line' + line);
salesOrder.setCurrentSublistValue({
sublistId: 'item',
fieldId: 'item',
value: 510,
ignoreFieldChange: true
});
salesOrder.setCurrentSublistValue({
sublistId: 'item',
fieldId: 'amount',
value: 100,
ignoreFieldChange: true
});
salesOrder.setCurrentSublistValue({
sublistId: 'item',
fieldId: 'taxcode',
value: -160,
ignoreFieldChange: true
});
salesOrder.commitLine({
sublistId: 'item'
});
var recId = salesOrder.save();
log.debug({
title: 'Record updated successfully',
details: 'Id: ' + recId
});
} catch (e) {
console.log('recordid' + recIdSO);
/*log.error({
title: e.name,
details: e.message
});*/
}
}
return {
pageInit: pageInit,
buttonclick: buttonclick
};
});

Related

Validating CollectionType array of object Symfony 4.2

I am creating a form that can hold undefined amount of collections of inputs inside, which appear after pressing button "Add". I was following this documentation : https://symfony.com/doc/current/form/form_collections.html and everything seems to work fine. I can add, delete, save and edit main form and collections of inputs. Now I am trying to validate inputs, but I am having problems with validating CollectionType entities.
For main form inputs I am using validation as following :
/**
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank
* #Assert\Type("string")
*/
private $name;
And for CollectionType my validation looks like:
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Tag", cascade={"persist"})
* #Assert\All({
* #Assert\Collection(
* fields = {
* "name" = {
* #Assert\NotBlank
* #Assert\Type("string")
* )
* },
* "desc" = {
* #Assert\NotBlank
* #Assert\Type("string")
* )
* }
* },
* allowMissingFields = false,
* allowExtraFields = false
* )
* })
*
*/
private $tags;
I am getting this error:
This value should be of type array|(Traversable&ArrayAccess).
I don't know if this helps, but by dumping form error I can see this :
-propertyPath: "data.tags[0]"
-invalidValue: Tag {#850 ▼
-id: null
-name: null
-desc: null
}
I have defined validation to Tag entity variables too, but it still doesn't work.
If I remove "#Assert\All" part I am getting :
This field is missing.
error for data.tags[name] and data.tags[desc].
As well as :
This field was not expected.
for data.tags[0]
EDIT:
I ended up creating custom validator according to https://symfony.com/doc/current/validation/custom_constraint.html
these rules:
* #Assert\All({
* #Assert\Collection(
* fields = {
* "name" = {
* #Assert\NotBlank
* #Assert\Type("string")
* )
* },
* "desc" = {
* #Assert\NotBlank
* #Assert\Type("string")
* )
* }
* },
* allowMissingFields = false,
* allowExtraFields = false
* )
* })
should be put in the Tag entity, on each field you want to be validated

How can I change api-platform {id} to a different name?

I am using api-platform with Symfony 4. It works fine, but I would like to change a GET url from:
/booking/{id} to /booking/{bookingId}
I'm using my own DTO object and custom data provider (not Doctrine ORM).
Here are the current #ApiResource and #ApiProperty definitions that work fine:
/**
*
* #ApiResource(
* itemOperations={
* "get"={
* "path"="/booking/{id}",
* },
* "api_bookings_get_item"={
* "swagger_context"={
* "operationId"="getBookingItem",
* "summary"="Retrieves details on a booking",
* "parameters"= {
* {
* "name"="id",
* "description"="Booking ID",
* "default"="15000",
* "in"="path",
* "required"=true,
* "type"="string"
* }
* },
* "responses"={
* "200"={
* "description"="Results retrieved"
* },
* "404"={
* "description"="Booking not found"
* }
* }
* }
* }
* },
* collectionOperations={}
* )
*/
final class Booking
{
/**
* #var string
* #Assert\NotBlank
*
* #ApiProperty(
* identifier=true,
* attributes={
* "swagger_context"={
* "description"="Booking ID",
* "required"=true,
* "type"="string",
* "example"="123456"
* }
* }
* }
*/
public $id;
// other variables
}
However, if I change all the references from 'id' to 'bookingId' it stops working and I get a 404 error. Here are the changes I made to the above code:
"path"="/booking/{bookingId}"
"name"="bookingId"
public $bookingId;
Is api-platform hard-coded to use 'id' as an identifier? Is there any way to change this?
In Api-platform the id parameter is hardcoded:
namespace ApiPlatform\Core\DataProvider;
private function extractIdentifiers(array $parameters, array $attributes)
{
if (isset($attributes['item_operation_name'])) {
if (!isset($parameters['id'])) {
throw new InvalidIdentifierException('Parameter "id" not found');
}
but you can create your own operation and use the parameter name that you want there is a great example in docs custom operations
You can customize the apiResource identifier via:
/**
* #ApiProperty(identifier=false)
*/
private $id;
and:
/**
* #ApiProperty(identifier=true)
*/
private $uuid;
where uuid is the new identifier to be used in request URLs.
For reference: symfonycasts did an excellent tutorial here:
https://symfonycasts.com/screencast/api-platform-extending/uuid-identifier

Netsuite suitescript 2.0

i want to create a confirmation message in view mode using message.create module i tried in the follwing way as shown in code:
first i took a user event script in view mode and added a button in before load and on click of the button a client script is triggered to create the message
/**
* #NApiVersion 2.x
* #NScriptType UserEventScript
* #NModuleScope SameAccount
*/
define(['N/ui/serverWidget'],
function(ui) {
/**
* Function definition to be triggered before record is loaded.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.newRecord - New record
* #param {string} scriptContext.type - Trigger type
* #param {Form} scriptContext.form - Current form
* #Since 2015.2
*/
function beforeLoad(scriptContext) {
if (scriptContext.type !== scriptContext.UserEventType.VIEW)
{
log.debug("triggered");
var Form=scriptContext.form;
Form.addButton({
id : 'custpage_message',
label : 'message',
functionName:'message'
});
form.clientScriptFileId = 18249;
}
}
return {
beforeLoad: beforeLoad,
};
});
this is my client script:
/**
* #NApiVersion 2.x
* #NScriptType ClientScript
* #NModuleScope SameAccount
*/
define(['N/ui/message'],
function(message) {
/**
* Function to be executed after page is initialized.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.currentRecord - Current form record
* #param {string} scriptContext.mode - The mode in which the record is being accessed (create, copy, or edit)
*
* #since 2015.2
*/
function pageInit(scriptContext) {
}
function message()
{
var myMsg = message.create({
title: "My Title",
message: "My Message",
type: message.Type.CONFIRMATION
});
}
return {
pageInit: pageInit,
message:message
};
});
I Thing Message function Conflicting. Just Rename the message function and try it.
var myMsg = message.create({
title: "My Title",
message: "My Message",
type: message.Type.CONFIRMATION
});
myMsg.show(); -- you missed that statement
-- In User event script
if (scriptContext.type !== scriptContext.UserEventType.VIEW)
{
log.debug("triggered");
var Form=scriptContext.form;
Form.addButton({
id : 'custpage_message',
label : 'message',
functionName:'testmessage()'
});
Form.clientScriptFileId = 115069;
}
as above code view mode but not created because u check the type not equal to view.
/**
* #NApiVersion 2.x
* #NScriptType ClientScript
* #NModuleScope SameAccount
*/
define(['N/ui/message'],
function(message) {
function pageInit(scriptContext) {
}
function testmessage()
{
debugger;
var myMsg = message.create({
title: "My Title",
message: "My Message",
type: message.Type.CONFIRMATION
});
myMsg.show();
}
return {
pageInit: pageInit,
testmessage:testmessage
};
});
/**
* #NApiVersion 2.x
* #NScriptType UserEventScript
* #NModuleScope SameAccount
*/
define([],
function() {
/**
* Function definition to be triggered before record is loaded.
*
* #param {Object} scriptContext
* #param {Record} scriptContext.newRecord - New record
* #param {string} scriptContext.type - Trigger type
* #param {Form} scriptContext.form - Current form
* #Since 2015.2
*/
function beforeLoad(scriptContext) {
if (scriptContext.type == scriptContext.UserEventType.VIEW)
{
log.debug("triggered");
var Form=scriptContext.form;
Form.addButton({
id : 'custpage_message',
label : 'message',
functionName:'testmessage'
});
Form.clientScriptFileId = 115069;
}
}
return {
beforeLoad: beforeLoad
};
});

Closure Compiler no longer able to determine type

I've been using Google's Closure Compiler for most of my projects, and a few of them in advanced mode with 100% typed.
One of my projects though no longer gets stated as 100% typed and I get warnings for things that I didn't used to get them for, and I can't seem to be able to figure out why. This is the message I get
WARNING - could not determine the type of this expression
v['children'].map(v => new _ChildLineItem(v['child'], v['option'], v['quantity'], v['price'])))))),
^
There's 42 more warnings like it, all about the same code, which I have here
/**
* #constructor
* #param {Array<!_Quote>} quotes
* #param {string} email
* #param {?string} quoteid
*/
function _GetQuotes(quotes, email, quoteid) {
this.quotes = quotes;
this.email = email;
this.quoteid = quoteid;
}
/**
* #constructor
* #param {string} quoteid
* #param {boolean} shipping
* #param {Array<!_Proof>} proofs
* #param {Array<!_LineItem>} lineitems
*/
function _Quote(quoteid, shipping, proofs, lineitems) {
this.quoteid = quoteid;
this.shipping = shipping;
this.proofs = proofs;
this.lineitems = lineitems;
}
/**
* #constructor
* #param {string} number
* #param {string} main
* #param {string} thumbnail
*/
function _Proof(number, main, thumbnail) {
this.number = number;
this.main = main;
this.thumbnail = thumbnail;
}
/**
* #constructor
* #param {string} name
* #param {number} quantity
* #param {number} price
* #param {Array<!_ChildLineItem>} children
* */
function _LineItem(name, quantity, price, children) {
this.name = name;
this.quantity = quantity;
this.price = price;
this.children = children;
}
/**
* #constructor
* #param {string} child
* #param {string} option
* #param {number} quantity
* #param {number} price
* */
function _ChildLineItem(child, option, quantity, price) {
this.child = child;
this.option = option;
this.quantity = quantity;
this.price = price;
}
Ajax({
url: '/ajax/getquotes',
data: Data,
success: function (/** !_GetQuotes */ data) {
var d = new _GetQuotes(
data['quotes'].map(v => new _Quote(v['quoteid'], v['shipping'],
v['proofs'].map(v => new _Proof(v['number'], v['main'], v['thumbnail'])),
v['lineitems'].map(v => new _LineItem(v['name'], v['quantity'], v['price'],
v['children'].map(v => new _ChildLineItem(v['child'], v['option'], v['quantity'], v['price'])))))),
data['email'], data['quoteid']);
...
I can rewrite the closures to specify the types of the objects coming through like this
v['children'].map(function( /** !_ChildLineItem */ v) { new _ChildLineItem(v['child'], v['option'], v['quantity'], v['price'])}
But shouldn't it be able to figure that out from the constructor definitions?
Actually me specifying all of them like this isn't even working
var d = new _GetQuotes(
data['quotes'].map((/** !_Quote */ v) => new _Quote(v['quoteid'], v['shipping'],
v['proofs'].map((/** !_Proof */ v) => new _Proof(v['number'], v['main'], v['thumbnail'])),
v['lineitems'].map((/** !_LineItem */ v) => new _LineItem(v['name'], v['quantity'], v['price'],
v['children'].map((/** !_ChildLineItem */ v) => new _ChildLineItem(v['child'], v['option'], v['quantity'], v['price'])))))),
data['email'], data['quoteid']);
With this warning
WARNING - could not determine the type of this expression
v['children'].map((/** !_ChildLineItem */ v) => new _ChildLineItem(v['child'], v['option'], v['quantity'], v['price'])))))),
^^^^^^^^^^^^^^^^^
The problem you are facing is that the compiler generally treats computed and dot property accesses differently. Computed property accesses are treated as the 'unknown' type unless you give a type signature the [] operator (i.e. the class implements IArrayLike as Array does or IObject[2] for map like objects).
Although, the compiler could understand that x.foo and x['foo'] are reference the same property, it does not currently do this.
[1] https://github.com/google/closure-compiler/wiki/Special-types-in-the-Closure-Type-System

FOSRestBundle: routes and annotations for parameters

I'm able to get GET parameters with #QueryParam() annotation, but it looks like it works for Query String data only: /user?id=123.
I'd prefer to have it like /user/123 instead. For this, I might use #Get("/user/{id}") annotation, but I don't see it has additional metadata which #QueryParam() has:
name="id", requirements="\d+", default="1", description="User id"
If I use both of the annotations, I get an error:
ParamFetcher parameter conflicts with a path parameter 'id' for route 'getone'
My conflicting docblock:
/**
* Finds and displays a Users entity.
*
* #Rest\View
* #Rest\Get("/user/{id}")
* #Rest\QueryParam(name="id", requirements="\d+", default="1", description="User id")
* #ApiDoc(section="Partner Users")
* #param int $id
* #return array
*/
PS I need to have an id in the path (/user/123), not in query, and I also need to use #QueryParam() as it's read by NelmioApiDocBundle. How may I resolve this issue?
FOSRestBundle's #Get annotation extends FOSRestBundle's #Route which in turn extends SensioFrameworkExtraBundle's #Route.
Have a look at the code and see the documentation chapter #Route and #Method.
The requirements and defaults attributes expect an array.
/**
* #Rest\View
* #Rest\Get("/user/{id}", requirements={"id" = "\d+"}, defaults={"id" = 1})
* #ApiDoc(
* description="Returns a User Object",
* parameters={
* {"name"="id", "dataType"="integer", "required"=true, "description"="User Id"}
* }
* )
*/
public function getAction($id)
{
// ...
}
If you want a description for requirements just do this in your annotation
/**
* #Rest\View
* #Rest\Get("/user/{id}", requirements={"id" = "\d+"}, defaults={"id" = 1})
* #ApiDoc(
* description="Returns a User Object",
* requirements={
* {"name"="id", "dataType"="integer", "required"=true, "description"="User Id"}
* }
* )
*/

Resources