In Genexus I have a variable and I want to pass the value to an attribute. Activate the Business Component but I get the following error when in the button I put the event of the procedure.
The procedure code:
&Mercaderia.Precio = &Precio
&Mercaderia.Save()
Did you made the load previous to the Update (Save)?:
&Mercaderia.Load(<MercaderiaPK>)
&Mercaderia.Precio = &Precio
&Mercaderia.Save()
Related
I am trying to use the Multiselect option set and want to build it dynamically, the addOption() is populating the multiselect field correctly, but on save of record it is prompting the validation error.
On change of contact lookup, I am populating the abc_multiselect field. It is populating fine dynamically then user selects the required options from multiselect field, but on save of record (Ribbon Save button not custom save event of form) the CRM is not accepting the values.
MSCRM Version
Server version: 9.2.22081.00182
Client version: 1.4.4647-2208.1
function polulate(executionContext){
var formContext = executionContext.getFormContext();
var multiselect = formContext.getControl("abc_multiselect");
var high = {value : 895390001, text : "High"};
multiselect.addOption(high);
}
The error is;
On Popup
One or more of the option values for this picklist are not in the range of allowed values.
Details
Exception Message: A validation error occurred. The value 895390001 of 'abc_multiselect' on record of type 'abc_ENTITY' is outside the valid range. Accepted Values:
ErrorCode: -2147204326
HexErrorCode: 0x8004431a
ErrorDetails:
ApiExceptionSourceKey: Plugin/Microsoft.Crm.ObjectModel.TargetAttributeValidationPlugin
ApiStepKey: fc743e7d-fbea-4695-bdb9-7d78334c8474
ApiDepthKey: 1
ApiActivityIdKey: 3907c6d7-ef4a-437e-946f-55e0f956fc3e
ApiPluginSolutionNameKey: System
ApiStepSolutionNameKey: System
ApiExceptionCategory: ClientError
ApiExceptionMessageName: PicklistValueOutOfRange
ApiExceptionHttpStatusCode: 400
HelpLink: http://go.microsoft.com/fwlink/?LinkID=398563&error=Microsoft.Crm.CrmException%3a8004431a&client=platform
TraceText:
[Microsoft.Crm.ObjectModel: Microsoft.Crm.ObjectModel.TargetAttributeValidationPlugin]
[fc743e7d-fbea-4695-bdb9-7d78334c8474: TargetAttributeValidationPlugin]
Activity Id: 28d5f67f-bf24-4eca-9124-cf95cf06dc30
I also tried to make all option set values hard coded (Added during the multiselect field creation), it worked smoothly. No issue ! But on dynamically population; on save, the CRM is not accepting the values.
I have tried this , this, this and this but all in vain.
Any one can guide, what is missing?
Update 1
function polulate(executionContext){
var formContext = executionContext.getFormContext();
var multiselect = formContext.getControl("abc_multiselect");
multiselect.clearOptions();
var high = {value : 895390001, text : "High"};
multiselect.addOption(high);
}
I also checked by changing the value from 895390001 to 895390000 and even to 100 and 101 but still same issue.
https://stackoverflow.com/a/48011975/5436880
This should solve your issue? most probably option set does not have 895390001 or it is already selected. you could also try to clear all option sets and then add
clear options
For using addOption those options need to be there in metadata first. You cannot add an option that is not present in metadata. Example can be Suppose you have Option A, 1 and 3 in Metadata. now you want to add another option 4 using Javascript "addOption", it is not possible.
In your case, get a maximum possible set or options in the optionset now onload of form or Onchange of field "removeOption " the options that are not required.
I am working on 2016 on-premise MSCRM, I need to check each record in bulk edit and alert the user if something is wrong, I tried to alert it from my update plugin but only general msg appeared, now I'm trying to get all records selected in bulk edit, I googled and found this code :
var formType = Xrm.Page.ui.getFormType();
if (formType == 6)
{
//Read ids from dialog arguments
var records = window.dialogArguments;
}
}
To use the bulk edit formtype I need to add to event onload or onchange on customizations.xml the attribute : BehaviorInBulkEditForm=“Enabled“ (unfortunately not so safe to edit this file) .
My questions:
which selected rows I'll get in onload and onchange event? ,I'm not sure where to use it in that case and if I'll get all the data I need.
Is there a better way/easy to get the data I need or to get the formtype - bulk edit.
Soon I'll be using MSCRM 365 is there any easier solution to this case in the 9.0 version ?
You can use method window.getDialogArguments(); to get ids.
Here is my example:
I added an onLoad event for my form and enabled the BehaviorInBulkEditForm.
function onLoad(formContext) {
var ids = window.getDialogArguments();
console.log(ids);
}
The ids is an array, every element is a selected record id.
['{335A56B7-C717-ED11-B83F-00224856D931}', '{CBBDEBFB-C717-ED11-B83F-00224856D931}', '{3607EFC7-C717-ED11-B83F-00224856D931}', '{325A56B7-C717-ED11-B83F-00224856D931}']
I have a couple of text fields that are filled from the database when the value of a select list changed.
I added another action to the list change dynamic action to execute PL/SQL code:
IF :P2_SELECT_LIST1 LIKE '%ABC%' AND :P2_NAME = 'WWW' THEN
:P2_NAME = NULL;
END IF;
Nothing happend on the page when I change the value of the select list, but the session value of P2_NAME gets cleared.
I also tried:
IF :P2_SELECT_LIST1 LIKE '%ABC%' AND :P2_NAME = 'WWW' THEN
:P2_NAME = '';
END IF;
But gotten the same result
In this dynamic action there are two fields next to your pl/sql code:
Items to submit: list the items here that you use in your pl/sql to get the values from session (if necessary, the values are usually already in session).
Items to return: list the items here that you need to refresh the value on the HTML page after some change in your pl/sql
I think this solves the problem.
Is it possible to update a record's attribute of picklist type with null by using Sdk.Sync.Update? It's not working for me.
Here's what I do:
var detailsObj = updatedDetailsObj; // I get updatedDetailsObj from previous logic, not shown here
var operation = new Sdk.Entity("kcl_operation");
operation.setId(operationId, false); // I have operationId from previous logic, not shown here
operation.addAttribute(new Sdk.String("op_updatedAccount", detailsObj.UpdatedAccount)); // works, get updated
operation.addAttribute(new Sdk.OptionSet("op_updatedExplanation", null)); // doesn't get updated
Sdk.Sync.update(operation);
After the completion of Sdk.Sync.update, the string field get updated, but the picklist field is left with its previous value, instead of null.
I also took a look inside the XML being sent inside Sdk.Sync.update, and indeed, it lacks the pair of "op_updatedExplanation" and null.
How can make it work?
Added:
I'm not doing it inside a form but inside a grid page, so that the user checks several records and I need to make the update on all of them.
standard CRM SDK code (assuming entity name and field name):
Entity operation = new Entity("kcl_operation");
operation.Id = operationId;
operation["op_updatedexplanation"] = null;
service.Update(operation);
where service is an IOrganizationService instance
Please use this snippet to set value as null.
Xrm.Page.getAttribute("op_updatedexplanation").setValue(null);
This will just set the value in the form. You may have to save the form to see the value getting stored in database.
Xrm.Page.data.entity.save();
If the control is disabled - you have to set the submitmode attribute also.
Xrm.Page.getAttribute("op_updatedexplanation").setSubmitMode("always");
jqGrid has employee name and employee id columns.
If employee name has changed, server validate method should called to validate name. Current row columns should be updated from data returned by this method.
If employee id has changed, server validate method should called to validate id.
Current row columns should be updated from data returned by this method.
Preferably jqGrid should stay in edit mode so that user has possibility to continue changing, accept or reject changes.
How to implement this in inline and form editing?
I'm thinking about following possibilites:
Possibility 1.
Use editrules with custom validator like
editrules = new
{
custom = true,
custom_func = function(value, colname) { ??? }
},
Issues: How to get data from all columns, make sync or async call and update columns with this call results.
Possibility 2.
Require user to press Enter key to save row.
Issues: how to find which column was changed and pass this column number to server.
How to update current row data from server response.
Possibility 3.
using blur as described in Oleg great answer in
jqgrid change cell value and stay in edit mode
Issues: blur does not fire if data is entered and enter is pressed immediately. How to apply blur in this case ?
In summary server sice calculation/validation should be dones as follows:
If column in changed and focus moved out or enter is pressed in changed column to save, server side sync or if not possible then async method should be called. Changed column name and current edited row values like in edit method are passed as parameters to this method.
This method returns new values for edited row. current edited row values should be replaced with values returned by that method.
Update
Oleg answer assumes that primary key is modified. This factor is not important. Here is new version of question without primary keys and other updates:
jqGrid has product barcode and product name columns.
If product name has changed, server validate method should called to validate name. Current row columns should be updated from data returned by this method.
If product barcode has changed, server validate method should called to validate product barcode.
Current row columns should be updated from data returned by this method.
jqGrid should stay in edit mode so that user has possibility to continue changing, accept or reject changes.
How to implement this in inline and form editing?
I'm thinking about following possibilites:
Possibility 1.
Use editrules with custom validator like
editrules = new
{
custom = true,
custom_func = function(value, colname) { ??? }
},
Issue: custom_func does not fire if input element loses focus. It is called before save for all elements. So it cannot used.
Possibility 2.
Require user to press Enter key to save row.
Issues: how to find which column was changed and pass this column number to server.
Save method should known column (name or barcode change order) and fill different columns. This looks not reasonable.
Possibility 3.
using blur:
colModel: [{"label":"ProductCode","name":"ProductCode","editoptions":{
"dataEvents":[
{"type":"focus","fn":function(e) { ischanged=false}},
{"type":"change","fn":function(e) {ischanged=true}},
{"type":"keydown","fn":function(e) {ischanged=true }},
{"type":"blur","fn":function(e) { if(ischanged) validate(e)} }
]},
To implement validate I found code from Oleg great answer in
jqgrid change cell value and stay in edit mode
Summary of requirement:
If column in changed and focus moved out or enter is pressed in changed column to save, server side sync or if not possible then async method should be called. Changed column name and current edited row values like in edit method are passed as parameters to this method.
This method returns new values for edited row. current edited row values should be replaced with values returned by that method.
Update2
This question is not about concurrency. This is single user and jqGrid issue. Updating means that single user changes product name or barcode and server shoudl provide additonal data (product id and/or name/barcode) is responce of this.
Update 4
I tried code below.
If user enters new code and presses Enter without moving to other row, blur does not occur and validation is not called.
How to dedect in jqGrid save method if cell is dirty or other idea how to force this code to run if enter is pressed to end edit without losing focus from changed foreign key cell ?
function validate(elem, column) {
ischanged = false;
var i, form, row;
var postData = { _column: column };
var colModel = $("#grid").jqGrid('getGridParam', 'colModel');
var formEdit = $(elem).is('.FormElement');
// todo: use jQuery serialize() ???
if (formEdit) {
form = $(elem).closest('form.FormGrid');
postData._rowid = $("#grid").jqGrid('getGridParam', 'selrow');
for (i = 0; i < colModel.length; i++)
eval('postData.' + colModel[i].name + '="' + $('#' + colModel[i].name + '.FormElement', form[0]).val() + '";');
}
else {
row = $(elem).closest('tr.jqgrow');
postData._rowid = row.attr('id');
for (i = 1; i < colModel.length; i++)
eval('postData.' + colModel[i].name + '="' + $('#' + postData._rowid + '_' + colModel[i].name).val() + '";');
}
$.ajax('Grid/Validate', {
data: postData,
async: false,
type: 'POST',
success: function (data, textStatus, jqXHR) {
for (i = 0; i < data.length; i++) {
if (formEdit)
$('#' + data[i].name + '.FormElement', form[0]).val(data[i].value);
else
$('#' + postData._rowid + '_' + data[i].name).val(data[i].value);
}
}
});
}
colModel is defined as:
{"name":"ProductBarCode",
"editoptions": {"dataEvents":
[{"type":"focus","fn":function(e) {ischanged=false}
},
{"type":"change","fn":function(e) {ischanged=true},
{"type":"keydown","fn":function(e) {if(realchangekey()) ischanged=true}
},{"type":"blur","fn":function(e) { if(ischanged) { validate( e.target,ProductBarCode')}}
}]},"editable":true}
It's one from the problems which is much easier to avoid as to eliminate. I have to remind you about my advises (in the comments to the answer) to use immutable primary key, so that is, will be never changed. The record of the database table can be destroyed, but no new record should have the id of ever deleted record.
On any concurrency control implementation it is important that the server will be first able to detect the concurrency problem. It can be that two (or more) users of your web application read the same information like the information about the employee. The information can be displayed in jqGrids for example. If you allow to change the employee id, than the first problem would be to detect concurrency error. Let us one user will change the employee id and another user will try to modify the same employee based on the previous loaded information. After the user submit the midification, the server application will just receive the "edit" request but will not find the corresponding record in the database. The server will have to sent error response without any detail. So the errorfunc of the editRow or the event handler errorTextFormat of the editGridRow should trigger "reloadGrid" reload the whole grid contain.
If you allow to edit the primary key, then I can imagine more dangerous situation as described before. It can be that another user not only change the id of the current editing row to another value, but one could change the id of one more record so, that its new id will be the same as the current editing id. In the case the request to save the row will overwrite another record.
To prevent such problems and to simplify the optimistic concurrency control one can add an additional column which represent any form of the timestamp in every table of the database which could be modified. I personally use Microsoft SQL Server and add I used to add the non-nullable column of the type rowversion (the same as the type timestamp in the previous version of the SQL Server). The value of the rowversion will be send to the jqGrid together with the data. The modification request which will be send to the server will contain the rowversion. If any data will be save in the database the corresponding value in the corresponding rowversion column will be automatically modified by the SQL database. In the way the server can very easy detect concurrency errors with the following code
CREATE PROCEDURE dbo.spEmployeesUpdate
-- #originalRowUpdateTimeStamp used for optimistic concurrency mechanism
-- it is the value which correspond the data used by the user as the source
#Id int,
#EmployeeName varchar(100),
#originalRowUpdateTimeStamp rowversion,
#NewRowUpdateTimeStamp rowversion OUTPUT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
-- ExecuteNonQuery() returns -1, but it is not an error
-- one should test #NewRowUpdateTimeStamp for DBNull
SET NOCOUNT ON;
UPDATE dbo.Employees
SET Name = #EmployeeName
WHERE Id=#Id AND RowUpdateTimeStamp=#originalRowUpdateTimeStamp;
-- get the new value of the RowUpdateTimeStamp (rowversion)
-- if the previous update took place
SET #NewRowUpdateTimeStamp = (SELECT RowUpdateTimeStamp
FROM dbo.Employees
WHERE ##ROWCOUNT > 0 AND Id=#Id)
END
You can verify in the code of the server application that the output parameter #NewRowUpdateTimeStamp will be set by the stored procedure dbo.spEmployeesUpdate. If it's not set the server application can throw DBConcurrencyException exception.
So in my opinion you should make modifications in the database and the servers application code to implement optimistic concurrency control. After that the server code should return response with HTTP error code in case of concurrency error. The errorfunc of the editRow or the event handler errorTextFormat of the editGridRow should reload the new values of the currently modified row. You can use either the more complex way or just reload the grid and continue the modification of the current row. In case of unchanged rowid you can easy find the new loaded row and to start it's editing after the grid reloading.
In the existing database you can use
ALTER TABLE dbo.Employees ADD NewId int IDENTITY NOT NULL
ALTER TABLE dbo.Employees ADD RowUpdateTimeStamp rowversion NOT NULL
ALTER TABLE dbo.Employees ADD CONSTRAINT UC_Employees_NewId UNIQUE NONCLUSTERED (NewId)
GO
Then you can use NewId instead of the id in the jqGrid or in any other place which you need. The NewId can coexist with your current primary key till you update other parts of your application to use more effective NewId.
UPDATED: I don't think that one really need to implement any complex error correction for the concurrency error. In the projects at my customers the data which are need be edited can not contain any long texts. So the simple message, which describe the reason why the current modifications could not be saved, is enough. The user can manually reload the full grid and verify the current contain of the row which he edited. One should not forget that any complex procedures can bring additional errors in the project, the implementation is complex, it extend the development budget and mostly the additional investment could never paid off.
If you do need implement automated refresh of the editing row I would never implement the cell validation in "on blur" event for example. Instead of that one can verify inside of errorfunc of the editRow or inside of the errorTextFormat event handler of the editGridRow that the server returns the concurrency error. In case of the concurrency error one can save the id of the current editing row in a variable which could be accessed inside of the loadComplete event handle. Then, after displaying of the error message, one can just reload the grid with respect of $('#list').trigger('reloadGrid',[{current:true}]) (see here). Inside of loadComplete event handle one can verify whether the variable of the aborted editing row is set. In the case one can call editRow or editGridRow and continue the editing of the string. I think that when the current row are changed another rows of the page could be also be changed. So reloading of the current page is better as reloading of the data only one current cell or one row of the grid.