Tridion Workflow Error on EDA_ITEMS_UPDATE Stored Procedure When Restarting WF Activities - workflow

I'm hitting an error whenever 2 or more instances of a workflow is restarted. We have suspended workflows during a network incident in our office(s). It was a script timeout that disrupted the process. I couldn't reproduce this timeout anymore so I attempted to restart the workflow activities to complete the process. However, when I restart more than 1 suspended activity, I get this error (the full log is farther below):
Cannot insert the value NULL into column 'ITEM_ID', table 'tridion_cm.dbo.ITEM_ASSOCIATIONS'; column does not allow nulls. INSERT fails.
The statement has been terminated.
Restarting activities and letting it finish one by one has no problem.
Full Event Log:
An error occurred while executing the Workflow script.
The Script Engine returned the following information:
SOURCE:
Line = 0
Column = 0
Number = -2147220673
Source = Component.Save
Description =
<?xml version="1.0" standalone="yes"?>
<tcm:Error ErrorCode="8004033F" Category="4" Source="Kernel" Severity="1" xmlns:tcm="http://www.tridion.com/ContentManager/5.0">
<tcm:Line ErrorCode="8004033F" Cause="false" MessageID="16137"><![CDATA[Unable to save Component (tcm:5-32795).]]>
<tcm:Token>RESID_4574</tcm:Token>
<tcm:Token>RESID_4418</tcm:Token>
<tcm:Token>tcm:5-32795</tcm:Token>
</tcm:Line>
<tcm:Line ErrorCode="8004033F" Cause="true">CDATA[No data found. ETA_ITEMS, U Cannot insert the value NULL into column 'ITEM_ID', table 'tridion_cm.dbo.ITEM_ASSOCIATIONS'; column does not allow nulls. INSERT fails. The statement has been terminated.
</tcm:Line>
<tcm:Line ErrorCode="8004033F" Cause="false"><![CDATA[A database error occurred while executing Stored Procedure "EDA_ITEMS_UPDATE".]]>
<tcm:Token>EDA_ITEMS_UPDATE</tcm:Token>
</tcm:Line>
<tcm:Details>
<tcm:CallStack>
<tcm:Location>System.Data.SqlClient.SqlConnection.OnError(SqlException,Boolean)</tcm:Location>
<tcm:Location>System.Data.SqlClient.SqlInternalConnection.OnError(SqlException,Boolean)</tcm:Location>
<tcm:Location>System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(</tcm:Location>
<tcm:Location>System.Data.SqlClient.TdsParser.Run(RunBehavior,SqlCommand,SqlDataReader,BulkCopySimpleResultSet,TdsParserStateObject)</tcm:Location>
<tcm:Location>System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader,RunBehavior,String)</tcm:Location>
<tcm:Location>System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior,RunBehavior,Boolean,Boolean)</tcm:Location>
<tcm:Location>System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior,RunBehavior,Boolean,String,DbAsyncResult)</tcm:Location>
<tcm:Location>System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult,String,Boolean)</tcm:Location>
<tcm:Location>System.Data.SqlClient.SqlCommand.ExecuteNonQuery()</tcm:Location>
<tcm:Location>Tridion.ContentManager.Data.AdoNet.DatabaseUtilities.ExecuteNonQuery(StoredProcedureInvocation)</tcm:Location>
<tcm:Location>Tridion.ContentManager.Data.AdoNet.Sql.SqlDatabaseUtilities.ExecuteNonQuery(StoredProcedureInvocation)</tcm:Location>
<tcm:Location>Tridion.ContentManager.Data.AdoNet.Sql.SqlDatabaseUtilities.ExecuteNonQuery(StoredProcedureInvocation)</tcm:Location>
<tcm:Location>Tridion.ContentManager.Data.AdoNet.IdentifiableObjectDataMapper.Tridion.ContentManager.Data.IIdentifiableObjectDataMapper.Update(IdentifiableObjectData)</tcm:Location>
<tcm:Location>Tridion.ContentManager.IdentifiableObject.Save(SaveEventArgs)</tcm:Location>
<tcm:Location>Tridion.ContentManager.ContentManagement.VersionedItem.Save(Boolean)</tcm:Location>
<tcm:Location>Tridion.ContentManager.ContentManagement.VersionedItem.Save()</tcm:Location>
<tcm:Location>Tridion.ContentManager.BLFacade.ContentManagement.VersionedItemFacade.UpdateAndCheckIn(UserContext,String,Boolean,Boolean)</tcm:Location>
<tcm:Location>XMLState.Save</tcm:Location>
<tcm:Location>Component.Save</tcm:Location>
</tcm:CallStack>
</tcm:Details>
</tcm:Error>
HelpFile =
HelpContext = 1000440
caused by: Component.Save
and description:
<?xml version="1.0" standalone="yes"?>
<tcm:Error ErrorCode="8004033F" Category="4" Source="Kernel" Severity="1" xmlns:tcm="http://www.tridion.com/ContentManager/5.0">
<tcm:Line ErrorCode="8004033F" Cause="false" MessageID="16137"><![CDATA[Unable to save Component (tcm:5-32795).]]>
<tcm:Token>RESID_4574</tcm:Token>
<tcm:Token>RESID_4418</tcm:Token>
<tcm:Token>tcm:5-32795</tcm:Token>
</tcm:Line>
<tcm:Line ErrorCode="8004033F" Cause="true">CDATA[No data found. ETA_ITEMS, U Cannot insert the value NULL into column 'ITEM_ID', table 'tridion_cm.dbo.ITEM_ASSOCIATIONS'; column does not allow nulls. INSERT fails. The statement has been terminated.
</tcm:Line>
<tcm:Line ErrorCode="8004033F" Cause="false"><![CDATA[A database error occurred while executing Stored Procedure "EDA_ITEMS_UPDATE".]]>
<tcm:Token>EDA_ITEMS_UPDATE</tcm:Token>
</tcm:Line>
<tcm:Details>
<tcm:CallStack>
<tcm:Location>System.Data.SqlClient.SqlConnection.OnError(SqlException,Boolean)</tcm:Location>
<tcm:Location>System.Data.SqlClient.SqlInternalConnection.OnError(SqlException,Boolean)</tcm:Location>
<tcm:Location>System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(</tcm:Location>
<tcm:Location>System.Data.SqlClient.TdsParser.Run(RunBehavior,SqlCommand,SqlDataReader,BulkCopySimpleResultSet,TdsParserStateObject)</tcm:Location>
<tcm:Location>System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader,RunBehavior,String)</tcm:Location>
<tcm:Location>System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior,RunBehavior,Boolean,Boolean)</tcm:Location>
<tcm:Location>System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior,RunBehavior,Boolean,String,DbAsyncResult)</tcm:Location>
<tcm:Location>System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult,String,Boolean)</tcm:Location>
<tcm:Location>System.Data.SqlClient.SqlCommand.ExecuteNonQuery()</tcm:Location>
<tcm:Location>Tridion.ContentManager.Data.AdoNet.DatabaseUtilities.ExecuteNonQuery(StoredProcedureInvocation)</tcm:Location>
<tcm:Location>Tridion.ContentManager.Data.AdoNet.Sql.SqlDatabaseUtilities.ExecuteNonQuery(StoredProcedureInvocation)</tcm:Location>
<tcm:Location>Tridion.ContentManager.Data.AdoNet.Sql.SqlDatabaseUtilities.ExecuteNonQuery(StoredProcedureInvocation)</tcm:Location>
<tcm:Location>Tridion.ContentManager.Data.AdoNet.IdentifiableObjectDataMapper.Tridion.ContentManager.Data.IIdentifiableObjectDataMapper.Update(IdentifiableObjectData)</tcm:Location>
<tcm:Location>Tridion.ContentManager.IdentifiableObject.Save(SaveEventArgs)</tcm:Location>
<tcm:Location>Tridion.ContentManager.ContentManagement.VersionedItem.Save(Boolean)</tcm:Location>
<tcm:Location>Tridion.ContentManager.ContentManagement.VersionedItem.Save()</tcm:Location>
<tcm:Location>Tridion.ContentManager.BLFacade.ContentManagement.VersionedItemFacade.UpdateAndCheckIn(UserContext,String,Boolean,Boolean)</tcm:Location>
<tcm:Location>XMLState.Save</tcm:Location>
<tcm:Location>Component.Save</tcm:Location>
</tcm:CallStack>
</tcm:Details>
</tcm:Error>
Source:
LogScriptError
Workflow Info
The components are newly created by another application through BusinessConnector. It performs revision saves before finishing the activity. The objective of the WF is to
assign the necessary component links based on text values from the other system;
create a page;
and publish it to a target.
I know this could've been done better by other means but it is how it is right now, please bear with the implementation.
Model / Schema
Product
- modelName (text)
- categoryName (text)
- categoryCL (component link)
- statusName (text)
- statusCL (component link)
- blah1 (text)
- blah2 (text)
- blah3 (text)
- blah4 (text)
Automatic Activity Script:
' COMPONENT WORKFLOW for PRODUCT SCHEMA (START)
' Load common functions
ExecuteGlobal oTDSE.GetObject("/webdav/200%20Design/Building%20Blocks/Library/Design/Workflow/Common/Workflow%20Functions.tbbs", 1).Content
' Initialize
Set objComp = CurrentWorkItem.GetItem()
Set oContentPub = objComp.Publication
Set oWebsitePub = oTDSE.GetObject(GetStagingLangPub("EN", "website1"), 1)
modelName = objComp.Fields.Item("modelName").Value.Item(1)
' Search and Assign component links based on some text fields from BC client
Set compListRowFilter = oTDSE.CreateListRowFilter()
Call compListRowFilter.SetCondition("ShowNewItems", TRUE)
Call compListRowFilter.SetCondition("ItemType", 16)
' Assign the first component link (categoryCL)
categoryName = objComp.Fields.Item("categoryName").Value.Item(1)
Set categoryComp = GetObjectFromFolder(categoryName, oTDSE.GetObject(categoryFolderWebDav, 1), compListRowFilter)
If Not categoryComp is Nothing Then
objComp.Fields.Item("categoryCL").Value.RemoveAll
objComp.Fields.Item("categoryCL").Value.Add(categoryComp)
Call objComp.Save(True)
Else
Call SendNoObjectEmail("Category", categoryName, "categoryCL", GetEmailsFromGroup(oContentPub, categoryOwner), "")
End if
' Assign the second component link (statusCL)
statusName = objComp.Fields.Item("statusName").Value.Item(1)
Set statusComp = GetObjectFromFolder(statusName, oTDSE.GetObject(statusFolderWebDav, 1), compListRowFilter)
If Not statusComp is Nothing Then
objComp.Fields.Item("statusCL").Value.RemoveAll
objComp.Fields.Item("statusCL").Value.Add(statusComp)
Call objComp.Save(True)
Else
Call SendNoObjectEmail("Status", statusName, "statusCL", GetEmailsFromGroup(oContentPub, statusOwner), "")
End if
' Create a page with the component in WF
Set oPage = CreateDefaultPage(modelName, oWebSitePub, SaveToFolderWebDav, PageTemplateWebDav)
Call oPage.ComponentPresentations.Add(oWebSitePub, oTDSE.GetObject(ComponentTemplateWebDav, 1))
oPage.Save(True)
' Publish the page
....
' Send email
....
' Finish the Activity
Call CurrentWorkItem.ActivityInstance.FinishActivity("someMsg")
' COMPONENT WORKFLOW for PRODUCT SCHEMA (END)
I haven't zoomed in to where exactly it happens but I would assume it is either the categoryCL or statusCL's objComp.Save(True) line.

I notice that you are calling Call objComp.Save(True) - The "True" parameter forces a "CheckIn", as it means "Done Editing".
It looks like this can occur twice (i.e. If there is a statusCL and a categoryCL) whichmay be causing the problem. I would try changing the behavior so that you only the call Save() method at the end if a change has been made, and not saving it twice.

From what I see the problem is that you are trying to assign nulls as component links. Are you sure that you are not trying to link these 2 components one to another? It might be that the link you are trying to create is to new component in workflow (it has no checked-in version yet), hence you can't create link to it. Try adding some logging to your script, so we could see where exactly does it fail and what are the parameters.
And I agree with Frank that this exception is worth a defect report.

Related

How to write a value to the workflow container

Below is my workflow inbox content
STEP 1: ln HTML table, the user can approve or reject WBS by checked radio
STEP 2: When user click confirm, then the workflow will update status for each WBS that shown in the table.
Purpose:
I need to get the user's action from step 1.
For now I've enhanced SAPEVENT and make it called my Function Module.
In my function function module I can use 'SAP_WAPI_READ_CONTAINER' properly but I cannot use 'SAP_WAPI_WRITE_CONTAINER' to write the value to workflow.
There was error msg 900 occurs.
Please see my code below:
DATA: LT_CONTAINER TYPE STANDARD TABLE OF SWR_CONT,
LT_MSG_LINES TYPE SAPI_MSG_LINES,
LT_MSG_STRUC TYPE SAPI_MSG_STRUC.
DATA: LV_RETCODE TYPE SY-SUBRC.
*** Get workflow container by using 'SAP_WAPI_READ_CONTAINER' ***
CALL FUNCTION 'SAP_WAPI_READ_CONTAINER'
EXPORTING
WORKITEM_ID = IV_WORKITEMID
IMPORTING
RETURN_CODE = LV_RETCODE
TABLES
SIMPLE_CONTAINER = LT_CONTAINER
MESSAGE_LINES = LT_MSG_LINES
MESSAGE_STRUCT = LT_MSG_STRUC.
*** then I change data inside LT_CONTAINER as my requirement below ***
REPLACE |type="submit" id="ALLAPPV"| IN TABLE LT_CONTAINER[]
WITH |type="button" id="ALLAPPV" hidden|.
*** use SAP_WAPI_WRITE_CONTAINER in order to write value to workflow container but it return error msg 900 ***
CALL FUNCTION 'SAP_WAPI_WRITE_CONTAINER'
EXPORTING
WORKITEM_ID = IV_WORKITEMID
DO_COMMIT = 'X'
OVERWRITE_TABLES_SIMPLE_CONT = 'X'
IMPORTING
RETURN_CODE = LV_RETCODE
TABLES
SIMPLE_CONTAINER = LT_CONTAINER
MESSAGE_LINES = LT_MSG_LINES
MESSAGE_STRUCT = LT_MSG_STRUC.

My google sheets function does the job when run from editor but gives different outcome when trigered by Form submit

I have a google form and a sheet that collects the responses which of course always appear at the bottom. I have been using the following script to copy the last response (which is always on the last row) from the Response sheet (Form Responses 2) to row two of another sheet (All Responses). When run by a trigger on Form Submit the script inserts a blank row into All Responses, then the copied values into another row above the blank row. Please can you help and tell me why and how I might change the script so the blank row is not added:
function CopyLastrowformresponse () {
var ss = SpreadsheetApp.getActive();
var AR = ss.getSheetByName("All Responses");
var FR = ss.getSheetByName("Form responses 2");
var FRlastrow = FR.getLastRow();
AR.insertRowBefore(2);
FR.getRange(FRlastrow, 1, FRlastrow, 22).copyTo(AR.getRange("A2"), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
}
A few things could be going on here.
You're getting a number of rows equal to FRlastrow, when I think you only want to be getting 1 row.
Apps Script has buggy behavior with onFormSubmit() triggers, so you may to check duplicate triggers (see this answer).
The script isn't fully exploiting the event object provided by onFormSubmit(). Specifically, rather than getting the last row from one sheet, you could use e.values, which is the same data.
I would change the script to be something like this:
function CopyLastrowformresponse (e) {
if (e.values && e.values[1] != "") { // assuming e.values[1] (the first question) is required
SpreadsheetApp.getActive()
.getSheetByName("All Responses")
.insertRowBefore(2)
.getRange(2, 1, 1, e.values.length)
.setValues([e.values]);
}
}
But, ultimately, if all you want to do is simply reverse the order of the results, then I'd ditch Apps Script altogether and just use the =SORT() function.
=SORT('Form responses 2'!A:V, 'Form responses 2'!A:A, FALSE)

How to pass a null value received on msg.req.query to msg.payload

I am developing an application using Dashdb on Bluemix and nodered, my PHP application uses the call to webservice to invoke the node-red, whenever my function on PHP invokes the node to insert on table and the field GEO_ID is null, the application fails, I understand the issue, it seems the third parameter was not informed, I have just tried to check the param before and passing something like NULL but it continues not working.
See the code:
msg.account_id = msg.req.query.account_id;
msg.user_id = msg.req.query.user_id;
msg.geo_id=msg.req.query.geo_id;
msg.payload = "INSERT INTO ACCOUNT_USER (ACCOUNT_ID, USER_ID, GEO_ID) VALUES (?,?,?) ";
return msg;
And on Dashdb component I have set the parameter as below:
msg.account_id,msg.user_id,msg.geo_id
The third geo_id is the issue, I have tried something like the code below:
if(msg.req.query.geo_id===null){msg.geo_id=null}
or
if(msg.req.query.geo_id===null){msg.geo_id="null"}
The error I got is the one below:
dashDB query node: Error: [IBM][CLI Driver][DB2/LINUXX8664] SQL0420N Invalid character found in a character string argument of the function "DECIMAL". SQLSTATE=22018
I really appreciate if someone could help me on it .
Thanks,
Eduardo Diogo Garcia
Is it possible that msg.req.query.geo_id is set to an empty string?
In that case neither if statement above would get executed, and you would be trying to insert an empty string into a DECIMAL column. Maybe try something like this:
if (! msg.req.query.geo_id || msg.req.query.geo_id == '') {
msg.geo_id = null;
}

Salesforce trigger - passing values from trigger record

Objective here – to extract the account__r.id from the record in the trigger and add the value to a list with each for-loop…
CODE SAMPLE ONE
if (trigger.isinsert) {
for (loan__c oloan : trigger.new){
system.debug(oloan);
Loan__c loanintrigger = new loan__c();
loanintrigger = [ SELECT loan__c.name,account__r.id from loan__c WHERE loan__c.id = : oloan.id];
Account a = new account ();
a.id = loanintrigger.account__r.id;
// debug to test insertion of value directly from trigger object.....
system.debug(a.id);
CODE SAMPLE TWO
if (trigger.isinsert) {
for (loan__c oloan : trigger.new){
system.debug(oloan);
Account a = new account ();
a.id = oloan.account__r.id;
// debug to test insertion of value directly from trigger object.....
system.debug(a.id);
So… trigger one runs and completes its assignment. Trigger two fails due to “missing argument.” I.e. – system.debug(a.id) shows that the id in the account object is null.
1, of course, requires a query for every record in the trigger. No style points.
This foreshadows another important and vexing question….
In the context of an after delete trigger, not even #1 works because you cannot query DB for data that has been deleted!
Can anybody suggest alternate approach? What am I missing?

Trigger works but test doesn't cover 75% of the code

I have a trigger which works in the sandbox. The workflow checks the field in the campaign level and compares it with the custom setting. If it matches, then it returns the target to the DS Multiplier field. The trigger looks as follows
trigger PopulateTarget on Campaign (before insert, before update)
{
for(Campaign campaign : Trigger.new)
{
if (String.isNotBlank(campaign.Apex_Calculator__c) == true)
{
DSTargets__c targetInstance = DSTargets__c.getInstance(campaign.Apex_Calculator__c);
{
String target = targetInstance .Target__c;
campaign.DS_Target_Multiplier__c = Target;
}
}
}
}
However, I had problems to write a proper test to this and asked for the help on the internet. I received the test
#isTest
private class testPopulateTarget{
static testMethod void testMethod1(){
// Load the Custom Settings
DSTargets__c testSetting = new DSTargets__c(Name='Africa - 10 Weeks; CW 10',Target__c='0.1538', SetupOwnerId = apexCalculatorUserId);
insert testSetting;
// Create Campaign. Since it would execute trigger, put it in start and stoptests
Test.startTest();
Campaign testCamp = new Campaign();
// populate all reqd. fields.
testCamp.Name = 'test DS campaign';
testCamp.RecordTypeId = '012200000001b3v';
testCamp.Started_Campaign_weeks_before_Event__c = '12 Weeks';
testCamp.ParentId= '701g0000000EZRk';
insert testCamp;
Test.stopTest();
testCamp = [Select ID,Apex_Calculator__c,DS_Target_Multiplier__c from Campaign where Id = :testCamp.Id];
system.assertEquals(testCamp.DS_Target_Multiplier__c,testSetting.Target__c);// assert that target is populated right
}
}
Such test returns the error "Compile Error: Variable does not exist: apexCalculatorUserId at line 6 column 122". If I remove that ApexCalculator part System.assertEquals then the test passes. However it covers 4/6 part of the code (which is 66%)
Could anyone help me how should I amend the code to make the coverage of 75%?
Yes, apexCalculatorUserId has not been defined. The code you were given appears to be incomplete. You'll need to look at the constructor DSTargets__c and see what kind of ID it is expecting there.
At a guess, you could try UserInfo.getUserId() to get the ID of the current user, but that may not be the ID that's expected in the constructor. It would be worth trying it to see if the test coverage improves.
1) Replace apexCalculatorUserId with UserInfo.getUserId()
2) I'm not sure what kind of field is Apex_Calculator__c on campaign. If its not a formula you want to insert a new line before "insert testCamp". Something like:
testCamp.Apex_Calculator__c = UserInfo.getUserId();