Netsuite suitescript 2.0 - suitescript

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
};
});

Related

Magento 2 Create Shipping Method with Delivery Options

I created a new shipping method for home delivery and I want to be able to present options such as a building/unit/room number. My thought was when the user selected this method the options would appear with a text field for the user to enter information before moving to the payment page. Any guidance on the best way to make this happen?
This is in Model/Carrier
/**
* #param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
* #param \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory
* #param \Psr\Log\LoggerInterface $logger
* #param \Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory
* #param \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory
* #param array $data
*/
public function __construct(
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
\Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory,
\Psr\Log\LoggerInterface $logger,
\Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory,
\Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory,
array $data = []
) {
$this->_rateResultFactory = $rateResultFactory;
$this->_rateMethodFactory = $rateMethodFactory;
$this->_logger = $logger;
parent::__construct($scopeConfig, $rateErrorFactory, $logger, $data);
}
/**
* #param RateRequest $request
* #return \Magento\Shipping\Model\Rate\Result|bool
*/
public function collectRates(RateRequest $request)
{
if (!$this->getConfigFlag('active')) {
return false;
}
/** #var \Magento\Shipping\Model\Rate\Result $result */
$result = $this->_rateResultFactory->create();
$shippingPrice = $this->getConfigData('price');
$method = $this->_rateMethodFactory->create();
$method->setCarrier($this->_code);
$method->setCarrierTitle($this->getConfigData('title'));
$method->setMethod($this->_code);
$method->setMethodTitle($this->getConfigData('name'));
$method->setPrice($shippingPrice);
$method->setCost($shippingPrice);
$result->append($method);
return $result;
}
/**
* #return array
*/
public function getAllowedMethods()
{
return [$this->_code=> $this->getConfigData('name')];
}
I tried to create checkout_index_index.xml and add a custom phtml page to it with
<script type="text/javascript">
require([
'jquery',
'Magento_Checkout/js/model/quote',
], function (jQuery, quote) {
jQuery(document).ready(function () {
quote.shippingMethod.subscribe(function (value) {
if (quote.shippingMethod() && quote.shippingMethod().carrier_code == 'your_custom_shipping_method_code') {
var customBlock = "<div class ='custom-information'><input type="text" id="your_custom_id"></div>";
if((!$('.custom-information').length > 0)) {
$('#checkout-shipping-method-load').append(customBlock);
}
});
});
});
});
</script>
And then I added in Model
namespace Magento\Checkout\Model;
class GuestShippingInformationManagement implements \Magento\Checkout\Api\GuestShippingInformationManagementInterface
{
/**
* #var \Magento\Quote\Model\QuoteIdMaskFactory
*/
protected $quoteIdMaskFactory;
/**
* #var \Magento\Checkout\Api\ShippingInformationManagementInterface
*/
protected $shippingInformationManagement;
/**
* #param \Magento\Quote\Model\QuoteIdMaskFactory $quoteIdMaskFactory
* #param \Magento\Checkout\Api\ShippingInformationManagementInterface $shippingInformationManagement
* #codeCoverageIgnore
*/
public function __construct(
\Magento\Quote\Model\QuoteIdMaskFactory $quoteIdMaskFactory,
\Magento\Checkout\Api\ShippingInformationManagementInterface $shippingInformationManagement
) {
$this->quoteIdMaskFactory = $quoteIdMaskFactory;
$this->shippingInformationManagement = $shippingInformationManagement;
}
/**
* {#inheritDoc}
*/
public function saveAddressInformation(
$cartId,
\Magento\Checkout\Api\Data\ShippingInformationInterface $addressInformation
) {
/** #var $quoteIdMask \Magento\Quote\Model\QuoteIdMask */
$quoteIdMask = $this->quoteIdMaskFactory->create()->load($cartId, 'masked_id');
return $this->shippingInformationManagement->saveAddressInformation(
$quoteIdMask->getQuoteId(),
$addressInformation
);
}
}
and started to modify it but I think I may be headed in the wrong direction,
Thoughts on the best way to accomplish this?
Thanks!

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

add button and update sales order using netsuite

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
};
});

Override Method or EventListener: stop creation process and show warning just the first time in EasyAdmin?

I am using EasyAdmin in my SF 3.3 project but I need to achieve something different from how EasyAdmin has been built for. Take a look at the following picture:
As you might notice a user can be in more than one GroupingRole. Having that information the challenge is:
Check if the user has been assigned to any other GroupingRole
If the criteria meets the condition then show a warning message saying "The user A is already assigned to GroupingRole A" and prevent the record to be created. (this message could be in a popup, a javascript alert or an alert from Bootstrap - since EA already uses it)
When the admin click once again on "Save changes" the record should be created.
What I want to achieve with this approach is to alert the admin that the user is already to any other group but not stop him for create the record.
I have achieve some part of it already by override the prePersist method for just that entity (see below):
class AdminController extends BaseAdminController
{
/**
* Check if the users has been assigned to any group
*/
protected function prePersistGroupingRoleEntity($entity)
{
$usersToGroupRoleEntities = $this->em->getRepository('CommonBundle:UsersToGroupRole')->findAll();
$usersToGroupRole = [];
/** #var UsersToGroupRole $groupRole */
foreach ($usersToGroupRoleEntities as $groupRole) {
$usersToGroupRole[$groupRole->getGroupingRoleId()][] = $groupRole->getUsersId();
}
$usersInGroup = [];
/** #var Users $userEntity */
foreach ($entity->getUsersInGroup() as $userEntity) {
foreach ($usersToGroupRole as $group => $users) {
if (\in_array($userEntity->getId(), $users, true)) {
$usersInGroup[$group][] = $userEntity->getId();
}
}
}
$groupingRoleEnt = $this->em->getRepository('CommonBundle:GroupingRole');
$usersEnt = $this->em->getRepository('CommonBundle:Users');
$message = [];
foreach ($usersInGroup as $group => $user) {
foreach($user as $usr) {
$message[] = sprintf(
'The user %s already exists in %s group!',
$usersEnt->find($usr)->getEmail(),
$groupingRoleEnt->find($group)->getName()
);
}
}
}
}
What I don't know is how to stop the record to be created and instead show the warning just the first time the button is clicked because the second time and having the warning in place I should allow to create the record.
Can any give me some ideas and/or suggestions?
UPDATE: adding entities information
In addition to the code displayed above here is the entities involved in such process:
/**
* #ORM\Entity
* #ORM\Table(name="grouping_role")
*/
class GroupingRole
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer",unique=true,nullable=false)
* #ORM\GeneratedValue
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="role_name", type="string", nullable=false)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="role_description", type="string", nullable=false)
*/
private $description;
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="Schneider\QuoteBundle\Entity\Distributor", inversedBy="groupingRole")
* #ORM\JoinTable(name="grouping_to_role",
* joinColumns={
* #ORM\JoinColumn(name="grouping_role_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="DistributorID", referencedColumnName="DistributorID", nullable=false)
* }
* )
*
* #Assert\Count(
* min = 1,
* minMessage = "You must select at least one Distributor"
* )
*/
private $distributorGroup;
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="CommonBundle\Entity\Users", inversedBy="usersGroup")
* #ORM\JoinTable(name="users_to_group_role",
* joinColumns={
* #ORM\JoinColumn(name="grouping_role_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="users_id", referencedColumnName="users_id", nullable=false)
* }
* )
*
* #Assert\Count(
* min = 1,
* minMessage = "You must select at least one user"
* )
*/
private $usersInGroup;
/**
* Constructor
*/
public function __construct()
{
$this->distributorGroup = new ArrayCollection();
$this->usersInGroup = new ArrayCollection();
}
}
/**
* #ORM\Entity()
* #ORM\Table(name="users_to_group_role")
*/
class UsersToGroupRole
{
/**
* #var int
*
* #ORM\Id()
* #ORM\Column(type="integer",nullable=false)
* #Assert\Type(type="integer")
* #Assert\NotNull()
*/
protected $usersId;
/**
* #var int
*
* #ORM\Id()
* #ORM\Column(type="integer", nullable=false)
* #Assert\Type(type="integer")
* #Assert\NotNull()
*/
protected $groupingRoleId;
}
A little example by using form validation approach in EasyAdminBundle:
class AdminController extends EasyAdminController
{
// ...
protected function create<EntityName>EntityFormBuilder($entity, $view)
{
$builder = parent::createEntityFormBuilder($entity, $view);
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$data = $event->getData();
$flag = false;
if (isset($data['flag'])) {
$flag = $data['flag'];
unset($data['flag']);
}
$key = md5(json_encode($data));
if ($flag !== $key) {
$event->getForm()->add('flag', HiddenType::class, ['mapped' => false]);
$data['flag'] = $key;
$event->setData($data);
}
});
return $builder;
}
protected function get<EntityName>EntityFormOptions($entity, $view)
{
$options = parent::getEntityFormOptions($entity, $view);
$options['validation_groups'] = function (FormInterface $form) {
if ($form->has('flag')) {
return ['Default', 'CheckUserGroup'];
}
return ['Default'];
};
$options['constraints'] = new Callback([
'callback' => function($entity, ExecutionContextInterface $context) {
// validate here and adds the violation if applicable.
$context->buildViolation('Warning!')
->atPath('<field>')
->addViolation();
},
'groups' => 'CheckUserGroup',
]);
return $options;
}
}
Note that PRE_SUBMIT event is triggered before the validation process happen.
The flag field is added (dynamically) the first time upon submitted the form, so the validation group CheckUserGroup is added and the callback constraint do its job. Later, the second time the submitted data contains the flag hash (if the data does not changes) the flag field is not added, so the validation group is not added either and the entity is saved (same if the callback constraint does not add the violation the first time).
Also (if you prefer) you can do all this inside a custom form type for the target entity.

How to refer to route name with FOSRestBundle

Candidate Controller
class DefaultController extends PreviewMeController
{
/**
* Complete registration process for candidate
*
* #ApiDoc(
* section="Candidate",
* tags={"common"},
* )
*
* #Rest\View()
* #Post("/ua/register/candidate/{token}")
*
* #param Request $request
* #return \FOS\RestBundle\View\View
*/
public function registerCandidateAction($token)
{
}
}
Candidate routing.yml
candidate_api_routes:
type: rest
prefix: /v1
resource: "CandidateBundle\Controller\DefaultController"
name_prefix: "api_1_c_"
AppBundle Controller
/**
* Register a new user on the website
*
* #ApiDoc(
* section="Common Functionalities",
* tags={"common"},
* requirements={
* {"name"="email", "dataType"="string", "description"="Email of user"},
* {"name"="username", "dataType"="string", "description"="Username. Keep this same as email address"},
* {"name"="first_name", "dataType"="string", "description"="First name of user"},
* {"name"="last_name", "dataType"="string", "description"="Last name of user"},
* {"name"="plainPassword", "dataType"="array", "requirement"="['first':'password','second':'password']", "description"="Plain password. Send as an array with 'first' and 'second' as array keys"},
* {"name"="user_type","dataType"="string","requirement"="employer|candidate","description"="Employer or candidate user type"}
* },
* statusCodes={
* 200 = "When user is successfully registered",
* 400="When there is a validation error in the registration process"
* }
* )
* #Post("/ua/register")
* #Rest\View()
*
* #param Request $request
* #return array|\FOS\RestBundle\View\View
*/
public function registerAction(Request $request)
{
/** #var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */
$formFactory = $this->get('fos_user.registration.form.factory');
/** #var UserManager $fos_userManager */
$fos_userManager = $this->get('fos_user.user_manager');
/** #var User $user */
$user = $fos_userManager->createUser();
$user->setEnabled(true);
$user->setUserType($request->request->get('user_type'));
//remove user_type from request so it's not forwarded to form
$request->request->remove('user_type');
$form = $formFactory->createForm();
$form->setData($user);
$form->submit($request->request->all());
if( $form->isValid() ){
$event = new UserEvent($user);
$dispatcher = $this->get('event_dispatcher');
$dispatcher->dispatch(PmEvents::REGISTRATION_SUCCESS, $event);
$fos_userManager->updateUser($user);
$wrapper = new PMResponseWrapper();
$wrapper->setData(array(
'ob_key' => $user->getObKey()
));
/** #var View $response */
$response = View::create($wrapper->getFormattedData());
$response->setLocation( $this->generateUrl('register_candidate') );
return $response;
}
return $this->view($form);
}
app/console debug:router dump
api_1_register POST ANY ANY /api/v1/ua/register
api_1_register_confirm_token POST ANY ANY /api/v1/ua/register/confirm_token/{token}
api_1_c_index GET ANY ANY /api/v1/index
api_1_c_register_candidate POST ANY ANY /api/v1/ua/register/candidate/{token}
Problem is even though registerCandidateAction shows up in debug:router, I am not able to call it with $this->generateUrl() in registerAction.
When I call this line $response->setNextUrl($this->generateUrl('register_candidate')); I get this error Unable to generate a URL for the named route \"register_candidate\" as such route does not exist.
Please help in finding what's wrong here.

Resources