Siebel NextRecord method is not moving to the next record - siebel

I have found a very weird behaviour in our Siebel 7.8 application. This is part of a business service:
var bo:BusObject;
var bc:BusComp;
try {
bo = TheApplication().GetBusObject("Service Request");
bc = bo.GetBusComp("Action");
bc.InvokeMethod("SetAdminMode", "TRUE");
bc.SetViewMode(AllView);
bc.ClearToQuery();
bc.SetSearchSpec("Status", "='Unscheduled' OR ='Scheduled' OR ='02'");
bc.ExecuteQuery(ForwardOnly);
var isRecord = bc.FirstRecord();
while (isRecord) {
log("Processing activity '" + bc.GetFieldValue("Id") + "'");
bc.SetFieldValue("Status", "03");
bc.WriteRecord();
isRecord = bc.NextRecord();
}
} catch (e) {
log("Exception: " + e.message);
} finally {
bc = null;
bo = null;
}
In the log file, we get something like this:
Processing activity '1-23456'
Processing activity '1-56789'
Processing activity '1-ABCDE'
Processing activity '1-ABCDE'
Exception: The selected record has been modified by another user since it was retrieved.
Please continue. (SBL-DAT-00523)
So, basically, it processes a few records from the BC and then, apparently at random, it "gets stuck". It's like the NextRecord call isn't executed, and instead it processes the same record again.
If I remove the SetFieldValue and WriteRecord to avoid the SBL-DAT-00523 error, it still shows some activities twice (only twice) in the log file.
What could be causing this behaviour?

It looks like in business component "Action" you have join(s) that can return multiple records for one base record and you use ForwardOnly mode to query BC.
Assume, for example, in table S_EVT_ACT you have one record with a custom column X_PHONE_NUMBER = '12345678' and you have two records in table S_CONTACT with column 'MAIN_PH_NUM' equal to the same value '12345678'. So when you will join these two tables using SQL like this:
SELECT T1.* FROM SIEBEL.S_EVT_ACT T1, SIEBELS_CONTACT T2
WHERE T1.X_PHONE_NUMBER = T2.MAIN_PH_NUM
as a result you will get two records, with the same T1.ROW_ID.
Exactly the same situation happens when you use ForwardOnly cursor mode in eScript, in this case Siebel just fetches everything what database has returned. And that why it's a big mistake to iterate over business component while it's queried in a ForwardOnly mode. You should use ForwardBackward mode instead, because in this case Siebel will exclude duplicates records (it also true for normal UI queries, because it also executed in ForwardBackward mode).
Actually this is the most important and less known difference between ForwardOnly and ForwardBackward cursor modes.

Try changing that query mode
bc.ExecuteQuery(ForwardOnly);
to ForwardBackward.

Related

Oracle APEX - how to read a cell from interactive grid

The same question once again but with (I hope) better explanation:
I created the most simple case:
An Interactive Grid IG with data source EMP ( table with 14 records contains Ename, Job, HireDate, Salary etc. etc.)
Text field P7_ENAME
After running it looks like below:
What I would like to do is to copy Ename from selected record of IG to P7_ENAME field .
I found several tutorials (text and video) how to do it. Most of them suggest to create dynamic action SelectionChange on IG and when TRUE add a JavaScript code something like below:
var v_ename;
model = this.data.model;
v_ename = model.getValue( this.data.selectedRecords[0], "Ename");
apex.item( "P7_ENAME" ).setValue (v_ename);
and the second step is to create another action: Refresh.
So finally I have a dynamic action with two steps : the first one is a Java script code and the second refresh function on my P7_ENAME field.
Sounds simple and it is simple to repeat/implement. A guy (I suppose) from India published a video on YouTube (https://www.youtube.com/watch?v=XuFz885Yndw) which I followed and in his case it works good. In my case it simple does not work - field P7ENAME is always empty, no errors appears. Any idea why ? Any hints, suggestion ?
thanks for any help
K.
The best way to debug and achieve what you are trying to do is as follows:
create the Dynamic action with the following setup:
-when -> selection change[interactive grid],
-selection type -> region, region -> your IG region,
-client side condition -> javascript expression: ```this.data.selectedRecords[0] != undefined```
First action of the true of the DA with the type: execute javascript code and fire on initialization is turned on, code: console.log(this.data.selectedRecords);
Run your page, and check the browser console. You should see an array of columns when you select a record from that IG as follows:
Find in that array, which sort number of the array contains the data that you want to use for the page item. Let's say I want the 3rd element which is "2694" then I should change my dynamic action's execute javascript code to:
var value = this.data.selectedRecords[0][2];
apex.item( "P7_ENAME" ).setValue (value);
The last thing I should do is add another true action (and the refresh action at the end) to the same dynamic action with type 'SET VALUE' and 'PLSQL EXPRESSION' as type, put :P7_ENAME in the expression, items to submit P7_ENAME and affected element: item / P7_ENAME as follows:

How I can make Mandatory add at least one row in Interactive grid in apex oracle

I have two region one form and one interactive grid like a master detail(company and company contact person ) how i can make the interactive grid mandatory ,the user can't submit page ,he/she need add at least one row in interactive grid ,
I can do that or I need to change the interactive grid to collection and count the row in validation
This one is a little tricky because of the way processes and validations work with Interactive Grids (they are executed once per submitted row). To work around this, I'll use a page item and a validation that works with it.
The basic idea of this solution is based on the fact that a new row will not have a primary key value. Here are the steps to reproduce (my example was on page 14, update the following as needed).
Create an Interactive Grid (IG) region. The primary key column should be Query Only (which ensures it's null for new rows).
Create a Hidden page item named P14_NULL_FOUND. Set Type under Server-side Condition to Never so that it never renders on the page.
Create an After Submit (before Validations) process. This process will NOT be associated with the IG so it will only fire once. Set the PL/SQL Code attribute to:
:P14_NULL_FOUND := 'NO';
That will clear out the value of the page item prior to the next process.
Create another After Submit process that runs just after the previous one. Set Editable Region to the IG. Then set the PL/SQL Code to something like the following:
if :PK_COLUMN_IN_IG is null
then
:P14_NULL_FOUND := 'YES';
end if;
You'll need to replace ":PK_COLUMN_IN_IG" with the name of the primary key column in the IG, such as ":EMPNO". This process will be run once for each submitted row in the IG. If a null value is found for the primary key column, then that would mean the user added a new row and the value of P14_NULL_FOUND would be set to 'YES'.
Create a new validation. This validation will NOT be associated with the IG so it will only fire once. Set Type to PL/SQL Expression. Set PL/SQL Expression to:
:P14_NULL_FOUND != 'NO'
Then set Error Message to something relevant.
At this point, you should be able to run the page and verify that the processes and validation are working correctly.
There is an another solution;
Create a page item like PX_ROWCOUNT which will hold the data of the row count of your IG.
Assign a static ID to your IG region.
Write a JS function to count the rows of the grid then set it to the page item. Sample function;
function f_setRowCount(){
var grid = apex.region("staticIDOfYourIG").widget().interactiveGrid("getViews", "grid");
var model = grid.model;
var rowCount = 0;
model.forEach(function (record) {
rowCount ++;
});
$s("PX_ROWCOUNT",rowCount);
}
To submit your page and run this function, change your submit button's behavior to Defined by Dynamic Action. Execute your function when user clicks to that button then submit your page via DA.
Add validation to Processing section of the page and check your page item there; PLSQL Expression => :PX_ROWCOUNT > 0
The solution by Hamit works nicely, except of the case of deletion of a row.
My suggestion is to amend the above code by adding inside the loop an if statement to check whether the row is editable or no.
So the code will be
var grid = apex.region("staticIDOfYourIG").widget().interactiveGrid("getViews", "grid");
var model = grid.model;
var rowCount = 0;
model.forEach(function (record) {
if (model.allowEdit(record)) {
rowCount ++;
}
});
$s("PX_ROWCOUNT",rowCount);

Big Query insertAll method in Java is not reflecting the changes in Table

I am trying to insert data into a Big Query table using the method insertAll.
This is how my insert method code looks like -
public void insertIntoTable(String datasetName, String tableName) {
TableId tableId = TableId.of(datasetName, tableName);
String fieldName = "testField";
Map<String, Object> rowContent = new HashMap<>();
rowContent.put(fieldName, "testVal");
InsertAllResponse response = bigquery.insertAll(InsertAllRequest.newBuilder(tableId).addRow("rowId", rowContent).build());
if (response.hasErrors()) {
for (Map.Entry<Long, List<BigQueryError>> entry : response.getInsertErrors().entrySet()) {
System.out.println(entry.getValue().toString());
}
}
}
Although it is not throwing any error, but still data is not getting inserted into my table.
As per there access-control document:
https://cloud.google.com/bigquery/docs/access-control
To use the insertAll method the user would require bigquery.tables.updateData permission. And for using that permission the user need to have the bigquery.dataEditor role, which I already have.
There is no issue with the permission because in another method I am creating a table, and the table is getting created successfully in Big Query.
The tableName and datasetName is also correct, I tested it in the debug mode.
Another issue that I can think of is type mismatch issue. But thats not the case either, because I checked it and the type is String only. Attached schema details below -
Can there be any other issue which I may be missing out here?
=============================================
Edited Part -
It has been pointed out to me that the stream data does not reflect in the UI, for viewing the data we have to query the table.
Now I am facing a new issue.
I executed the above function 6 times, each time with a different value. Basically I was changing only this line -
rowContent.put(fieldName, "testVal");
The first time when I executed the method, the insert value was testVal.
The other five times when I executed the method, I modified this line of code -
Execution 1: rowContent.put(fieldName, "testVal1");
Execution 2: rowContent.put(fieldName, "testVal2");
Execution 3: rowContent.put(fieldName, "testVal3");
Execution 4: rowContent.put(fieldName, "testVal4");
Execution 5: rowContent.put(fieldName, "testVal5");
So ideally in my table there should be 6 rows with the values -
testVal
testVal1
testVal2
testVal3
testVal4
testVal5
But I am able to see only two rows when I am querying my table.
Why it is showing only 2 rows instead of 6?

CRM 2015 Microsoft.Xrm.Sdk: Unexpected results in second CreateQuery call

Microsoft.Xrm.Sdk, Version=7.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
var ctx = new ServiceContext(...);
var result1 = (from f in ctx.CreateQuery<aEntity>()
where f.field1 == "x"
select new { f.Id, f.field2 }).ToList();
var result2 = (from f in ctx.CreateQuery<aEntity>()
where f.field1 == "x"
select f.field1).First();
result2 returns null! After adding f.field1 to the select clause in the first query result2 returns "x". It looks like a internal columnset is created and used in the context of the second call. Looking at the SQL Server trace of both calls we see the expected select-from queries based on the C# code. The returned second result is not expected. Can someone explain this behaviour?
As for me it looks like a caching functionality and it's on the side of CRM because as you mentioned SQL queries were correct. I had the same issue in my applications when tried to make two consecutive queries for the same entity record but selected two different fields, the second request always returned NULL. Here are workarounds that I use when work with the ServiceContext:
Simple one: always retrieve an entity with all fields (without select statement) (even if I want it or not)
or create a service context with disabled caching
Right now I try to use the ServiceContext as less as possible replacing it with QueryBase expressions (even if I love to use LINQ).
Keep in mind LINQ CRM driver implementation is a subset of SQL only.
Could you try something like this?
var result1 = (from f in ctx.CreateQuery<aEntity>()
where f.field1 == "x"
select new CustomClass {
Id = f.aEntityId,
Field2 = f.field2
}).ToList();
You can have complex queries if you want, but you need to know what can be done and what can't be done.
Id property is not always returned by the driver, but the entity's primary key is, which is normally the entity logical name + "Id".

Set Form Values From Interactive Report Row

I have two tables: T1 and T2
Table T1 contains five columns: CT11, CT12, CT13, CT14, CT15
Table T2 contains 4 columns: CT21, CT22, CT23, CT24
I have a page with two regions. Region 1 is a Form on a Table using table T1. Region 2 is an Interactive Report ( IR ) on table T2.
The SQL for region 2 is:
select apex_item.RADIOGROUP(p_idx => 1, p_value => CT21) "Choose", CT22, CT23, CT24 from T2;
When the user click a radio button for a row in the Interactive Report, I would like the values in cells CT22, CT23, CT24 to populate
the CT13, CT14, CT15 fields in the table form in region 1. If a user clicks another row radio button, the values should update.
Using Google and Stackoverflow, I have tried a bunch of options, but I just can't seem to find the correct method.
Hopefully you understand how the f## arrays work a bit and what they look like in the generated html. To solve this you need javascript, and to understand that you do need to know what that html looks like. Nothing is as error-prone as misunderstanding this! Changing the f## array or any of the required columns name may stop the code from working properly, and while I don't think it is too hard, it is simply important to understand it. If you implement it, you will need to be able to support it and not rely on the help of a stranger on the internets. Having said that.
Create a dynamic action.
Event: After refresh
Selection type: Region
Region: your IR region
Condition: -
True action:
Execute javascript code
fire on page load: checked
Code:
$("input[name=f01]", this.triggeringElement).change(function(){
//mind the capitalization in the headers attribute selector
var lRow = $(this).closest("tr"),
lct22 = lRow.find("td[headers=CT22]").text(),
lct23 = lRow.find("td[headers=CT23]").text(),
lct24 = lRow.find("td[headers=CT24]").text();
console.log("22: " + lct22 + " - 23: " + lct23 + " - 24: " + lct24);
$s("P3_CT13", lct22);
$s("P3_CT14", lct23);
$s("P3_CT15", lct24);
})
"input[name=f01]" will select the radiobuttons. You gave the radiobuttons p_idx=>1 and this will translate to the usage of array f01.
I made a small demo app too: http://apex.oracle.com/pls/apex/f?p=11964 . Log in with apex_demo/demo