Undo form changes when an exception occurs - javascript

In Dynamics CRM 2013 is it possible to revert a field changed on a form when a business process error occurs?
For example:
1. User changes a text field on a form from 'abc' to 'xyz'
2. User clicks save
3. CRM pre-operation plugin validates field, 'xyz' not allowed, exception thrown
4. CRM displays business process error to user, 'xyz' is not allowed
5. The value 'xyz' is still shown in the form
The desired behavior we want is 'xyz' to revert to 'abc' in step 5.

You will need to cache the data first. You can do this OnLoad, e.g. by memorizing the entity's attribute values:
function GetInitialAttributeState() {
var preImage = {};
Xrm.Page.data.entity.attributes.forEach(function(field) {
// TODO: for lookup attributes you need to do extra work in order to avoid making merely a copy of an object reference.
preImage[field.getName()] = field.getValue();
});
return preImage;
}
window.preImage = GetInitialAttributeState();
Then you need to perform the save operation through the Xrm.Page.data.save method. Pass a callback function handling errors and resetting the fields, e.g.
Xrm.Page.data.save().then(
function() {
/* Handle success here. */
window.preImage = getInitialAttributeState();
},
function() {
/* Handle errors here. */
Xrm.Page.data.entity.attributes.forEach(function(field) {
if (field.getIsDirty()) {
field.setValue(preImage[field.getName()]);
}
});
});
It is not possible to reset the form's fields this way using the save event, because it kicks in before the actual save operation, never after it.

Why let the user save the record at all?
You could use a Business Ruel to validate the field, and set an error condition against the field for values you don't "like". The error condition will persist and prevent them saving the record until they change the value. The error message can give them some explanation as to why their value is not valid.
Obviously the validation you can do in a Business Rule is limited, but your example does not make it clear on what basis we match "xyz" as a "bad" value.

Related

An nlobjSearchFilter contains an invalid operator, or is not in proper syntax: internalid. Error while using record.submitFields

I have tried passing various values to id. Whenever a wrong internalid is passed I am getting a "That record does not exist." but when a valid internal id is passed the script throws "An nlobjSearchFilter contains an invalid operator, or is not in proper syntax: internalid."
var recordID = record.submitFields({
"type": record.Type.SALES_ORDER,
"id":9837,
"values": {
"memo": "MEMOTXT"
}
});
There’s nothing wrong with the code that you’ve shown. It’s likely that this error is caused by another script on the sales order record that isn’t expecting an XEDIT event, which is what record.submitFields() will trigger.
During an XEDIT event, user event scripts only have access to some basic fields about the record and the fields that were submitted. They don’t have access to every field on the record.
If the other script isn’t expecting this scenario, they may be trying to perform an action, but some expected values are null because they’re not available to the user event script.
The record.submitFields method has an optional parameter named "enablesourcing". Try running it with it as false.
var recordID = record.submitFields({
type: record.Type.SALES_ORDER,
id: 9837,
values: {
memo: 'MEMOTXT'
},
options: {
enableSourcing: false, //default is true
ignoreMandatoryFields : true //default is false
}
});
The code is fine, there is a User Event on Salesorder that you need to track down, period

Debugging script Netsuite suitescript in Chrome exception

So I am trying to add a button to the Sales Order form in netsuite that validates certain fields based on what you entered in previous fields. I am having trouble testing and debugging this in google chrome in netsuite. First, here is my code: I am adding the button that calls this function within the client script record.
function vsoeValidate(){
var calc = nlapiGetFieldValue('custbody_cv_vsoe_calculation');
calc = nlapiGetFieldValue('custbody_cv_renewal_rev_amount') - (nlapiGetFieldValue('custbody_cv_vsoe_cola') * nlapiGetFieldValue(1-'custbody_cv_vsoe_partner_disc')) - (nlapiGetFieldValue('custbody_cv_vsoe_bts_fees') * (1-nlapiGetFieldValue('custbody_cv_vsoe_partner_disc'))) /
(nlapiGetFieldValue('custbody_cv_vsoe_software_amt') * (1- nlapiGetFieldValue('custbody_cv_vsoe_multiyear_disc')));
nlapiSetFieldValue('custbody_cv_vsoe_calculation', calc);
var display = nlapiGetFieldValue('custbody_cv_vsoe_calculation_disp');
var bucket = nlapiGetFieldValue('custbody_cv_vsoe_bucket');
if(bucket === 'X'){
return false;
}
if(calc > (nlapiGetFieldValue('custbody_cv_vsoe_bucket.custrecord_cv_vsoe_maintenance_rate') *1.15) || calc < ('custbody_cv_vsoe_bucket.custrecord_cv_vsoe_maintenance_rate'*0.85)){
display = '<div style="; background-color:red; color:white;font-weight:bold;text-align:center">Out of bounds</div>';
return true;
}
else{
display = '<div style="; background-color:green; color:white;font-weight:bold;text-align:center">In bounds</div>';
return true;
}
}
when I click the button I get the error TypeError undefined is not a function.
I am really not sure where to go from here, is it because the logic inside vsoeValidate is not right or am I using the wrong type of function? Any help would be great thank you!
Update: here is the screenshot of my script record!
Try passing the function name as string i.e.
form.addButton('custpage_validatevsoe', 'Validate VSOE', 'vsoeValidate');
You mentioned that you set vsoeValidate as a validateField function. Do you want this function to run when users click the button or when NetSuite's valdiateField event is fired (upon field change, before the value is stored)?
If you want this to run on NetSuite's validateField event, then the function must return true or false; it cannot return void. Right now in your logic, you have:
if (bucket = 'x') {
return;
}
if (bucket = 'x') is an assignment operation, not an equality check. This assignment operation will return 'x', which is a truthy value, so your code will enter that if-block. You then return void (undefined), so my guess is that NetSuite is trying to do something with the result returned by your function but cannot because it returned undefined.
The validateField function also gets passed a parameter that provides the ID of the field being validated.
You will also want to inject some console logging at various points so you can figure out where your script is failing instead of just trying to guess at reading syntax.
Can you provide us with a screenshot of your Script record setup?
Also since you are using a client side script, you don't need to use the pageInit event for adding a custom button.
There is a 'Buttons' subtab, under the 'Scripts' tab when you create the Script record in NetSuite. This subtab is next to the 'Libraries' subtab.
There are two columns here, 'Label' and 'Function'.
So in your case, you can just put 'Validate VSOE' in the Label field and vsoeValidate in the Function field.
Please note that if you do it this way, the button will only be shown when you are creating or editing a record.

knockoutjs: multiple valueUpdates and choose which update happend

I have an input field with a value binding. The binding references a writable computed observable.
The intended behavior is:
a user types some letters: The writable computed should recognize the user is still typing
a user finishes typing: The writable should save the value
However, I only can choose updateValue: 'input' or updateValue: 'keydown', but not both. In the JavaScript code, the writable computed is not able to check which event occurred.
I need this behavior for this reason: if the user is just typing I want to fill an auto-suggestion box and want to bold the already typed letters. For this I need the keydown event. But if the user finishes typing (losing focus) I want to do some verification and then save the entered value.
If I use input the verification is working but the auto-suggestion feature is broken. If I use keydown the user isn't able to type something, because the verification always fails and the user cannot finish typing.
Suppose you have computed named self.inputValue which references to the input field.
You can apply rateLimit extender on perticular computed..
e.g.
self.inputValue = ko.computed(function(){
//perform the required logic
}).extend({ rateLimit: { method: "notifyWhenChangesStop", timeout: 400 } });;
where timeout is in miliseconds. It will hold the updation or notification of value for 400 miliseconds.
This should solve your problem. A small JSFiddle I've made for you : http://jsfiddle.net/rahulrulez/x8jmcpLh/
rateLimit was introduced in KO 3.1.0, if you are using old library then use throttle instead.

Task "Regarding" field not populating from subgrid in CRM 2013

I am using CRM 2013 on-premise with UR1 installed
I have a custom entity with a subgrid on it looking at related "tasks" which looks like this:
Whenever I create a task from the subgrid using the "+" button in the top right hand corner of the subgrid; the "Regarding" field of the newly created task remains blank. When it should be populated by a lookup to the record it was created from.
I have javascript on the task entity which checks the "Regarding" field to check what kind of entity it was created from (if it was created from one) and gets certain field values from the calling entity to populate fields on the task.
Since the "Regarding" field is never filled the Javascript never fires - and the fields do not populate.
When the record is saved, if the regarding field is blank (I have not manually filled it in) - it will eventually be populated by the correct record about 10 - 15 seconds later if you refresh the page. Then the correct fields will be populated and the user is able to edit the option set values and save again. This is not ideal for the user as they would like it to be one fluid action.
Is there any way around this problem?
EDIT for future browsers of this question:
Found a partial work around. If you use an "Activity" subgrid rather than a "Task" subgrid the field will populate. This has a drawback though as you cannot edit the "Activity" subgrid's view to show "Task" specific fields.
Ran into this same issue. The way I got around it was to add a look-up to the custom entity on the form (we put this on a hidden tab). When the Task gets created from the custom entity the look-up will be populated. You can then use that look-up to grab the values that you need to populate, including the regarding field. Not the most elegant, but it works.
I also ran into this problem and went with a pure JS approach to resolving. On load of the task form, call populateRegarding().
This works because even though the regarding lookup doesn't populate by default, the query string parameters include _CreateFromType and _CreateFromId values.
This works in 2015, didn't test on earlier versions. Note that it is unsupported.
function populateRegarding() {
var regarding = Xrm.Page.getAttribute("regardingobjectid"),
createFromType = Xrm.Page.context.getQueryStringParameters()._CreateFromType,
createFromId = Xrm.Page.context.getQueryStringParameters()._CreateFromId;
if (!createFromId || !createFromType ||
!regarding || regarding.getValue() !== null) {
return;
}
var entityLogicalName = getEntityLogicalNameFromObjectTypeCode(createFromType);
regarding.setValue([{
id: createFromId,
entityType: entityLogicalName,
name: "Hardcoded Name" // TODO: retrieve name dynamically
}]);
}
// This method uses an undocumented object and is therefore unsupported.
// You could implement a supported version of this function by querying for
// metadata, but that would be very expensive.
function getEntityLogicalNameFromObjectTypeCode(otc) {
var map = Mscrm.EntityPropUtil.EntityTypeName2CodeMap,
logicalName;
otc = Number(otc); // convert string to number
for (logicalName in map) {
if (!map.hasOwnProperty(logicalName)) { continue; }
if (map[logicalName] === otc) {
return logicalName;
}
}
}

Force a SaveForm in Dynamics CRM 2011 using JavaScript

In CRM 2011, I want to be able to force a save of a form. My requirement is as below.
When the user opens the form, I check if a particular field is empty or not.
If it is empty, I want to force a save, thereby triggering a plugin which will set the field.
My current code is as below. This is in the FormOnLoad function which is associated with LoadForm.
if(checkfield == null){
Namespace.Functions.Contact.FormOnSave();
}
else{
// do nothing.
}
Now, this fails with the following line
Can't execute code from a freed script.
I have no clue as to how to proceed further. As a temporary fix, what I have done is the following
if(checkfield == null){
setTimeout('Namespace.Functions.Contact.FormOnSave()',4000);
}
else{
// do nothing.
}
Any advice on why this is happening and how can I fix it without the timeout would be really helpful?
I have had a look at this question and this, but in my case the form does get loaded or has it actually not yet loaded?
Either way, how can I force a save of a CRM form?
You can save the Form by using:
parent.Xrm.Page.data.entity.save();
But It will only trigger if
(Xrm.Page.data.entity.getIsDirty() == true)
However there is a workaround to set IsDirty property true.
Run the Javascript function on PageLoad. Try to update the value of the field which you are planning to populate through plugin:
For e.g.
function OnPageLoad()
{
var yourField = Xrm.Page.getAttribute("new_yourfield").getValue();
if(yourField == null)
{
parent.Xrm.Page.getAttribute("new_yourfield").setValue("any value");
parent.Xrm.Page.getAttribute("new_yourfield").setSubmitMode("always");
parent.Xrm.Page.data.entity.save();
}
}
You could skip all of this mumble jumbo with loading your form twice with Javascript by creating a plugin registered on the read or the entity. (Or better yet and if possible, on the Create of the entity)
The Plugin would just check for the field, and if it's empty, perform whatever logic you need it to to update the field, then save it, and return the updated field (or possibly just populate the value and don't update the database until the user performs the save)

Categories