Skip to Content

Problem

When you debug in C4C, you have an output window where you can see the sequence all the various script files are called. However, inside the ABSL script file you don’t have access to this information (otherwise known as a stack trace). Your ABSL file is blind – it does not know when it is called.

Example: When you link one document to another, various script files are called, e.g.:

  • BusinessTransactionDocument-AfterModify
  • BusinessTransactionDocument-Before Save
  • Root-AfterModify
  • Root-BeforeSave

… at best not only for the root document but also for the target document.

Wouldn’t it be great if you had the ability to pass information between those ABSL calls?

In my case, I was faced with a very complicated bug in C4C (due to be fixed in 1802) that lead to an infinite loop when changing two quotes in the same roundtrip.

In an on-premise system, I would have set a static global variable in the first roundtrip and in the second loop it would still be set so that I can see I’ve been here and break the loop. In C4C, this is not possible.

Solution

I ended up creating a separate BO that stores the UUID of the object, the node name and the change time. Luckily, the changetime stays the same during the infinite loop, so I am able to detect whether the same ABSL file is called twice in the same roundtrip.

Business Object

import AP.Common.GDT as apCommonGDT;

businessobject InfiniteLoopDetection {

        // In this BO, we register the UUID and the last change time of an object
	// if an infinite loop happens we can detect it b/c an entry for UUID and time already exists
		[AlternativeKey] element ObjectUUIDandNode : LANGUAGEINDEPENDENT_EXTENDED_Text;
		element ChangeDateTime : GLOBAL_DateTime;
}

The BO does not have any script files, but I created a reuse function that maintains the BO:

Reuse Function


import ABSL;
import AP.Common.GDT;

// checks if the supplied object uuid and time are already contained in the 
// InfiniteLoopDetection BO and returns true if it is
// if not, the entry is added if the IV_UPDATE parameter is true
var result : DataType::Indicator=false;
//return result;
// combine UUID and node into the key field (system supports only one unique key)
var key = IV_OBJECT_UUID.content.ToString() + IV_NODE;

var il = InfiniteLoopDetection.Retrieve(key);  

if (il.IsSet())
{
	if (il.ChangeDateTime == IV_DATETIME)
	{
		// loop detected, return true
		result = true;
	}
	else if (IV_UPDATE == true)
	{
		// otherwise update time, return false
		il.ChangeDateTime = IV_DATETIME;
		result = false;
	}
}
else if (IV_UPDATE == true)
{
	var il_node : elementsof InfiniteLoopDetection;
	il_node.ObjectUUID.content = Library::UUID.Generate(); // needed b/c save fails if missing (alternative key)
	il_node.ObjectUUIDandNode = key;
	il_node.ChangeDateTime = IV_DATETIME;
	il = InfiniteLoopDetection.Create(il_node);
	// this must work - otherwise dump
	var oid = il.ObjectUUIDandNode;
	result = false;
}

return result;

Parameters:

  • IV_OBJECT_UUID: UUID of the Object you want to register as touched
  • IV_NODE: Node of the Object you have touched (Root, Party, etc.)
  • IV_DATETIME: Time the object was changed
  • IV_UPDATE: If set to true: creates a new entry if it doesn’t exist/updates an existing entry

ABSL file

Now in each ABSL file that needs to access this global data, I just call the following code:

if (RULHelperFunctions.InfiniteLoopCheckAndUpdate(this.UUID, this.SystemAdministrativeData.LastChangeDateTime, "CustomerQuote.Root.BeforeSave", true) == true)
{
	return;
}

This example is for CustomerQuote. The field and node names could differ for other business objects.

How it works

In my case, I just needed to check whether an object, identified by UUID, node name and change time was already touched in the same roundtrip.

So in the debugger I had identified which ABSL files are involved in my infinite loop. In those files, I put the above ABSL code at the top. During the first roundtrip, the helper BO is filled with the document’s UUID, the current node and the change time of the document. If there is no entry, the ABSL file (BeforeSave etc.) is executed normally. However, during the second loop in the same roundtrip, the entry in the helper BO will already exist as the Change time remains the same. In this case I exit the ABSL file (BeforeSave etc.) with the return statement and this breaks the loop.

Storing data

You can easily add more field to the custom BO. This way you will not only check whether an entry exists, but can also pass more elaborate information between ABSL calls.

Alternatives

It has been suggested to use Transient fields. However, the transient field is not cleared automatically after the round trip. Meaning that during the next roundtrip the transient field will have the same value. It is only cleared if you press Cancel on the UI.

But you can use this to your advantage: If you want to store temporary global data that is persistent during roundtrips, but reset when you leave the screen, use transient fields.

If you need the temporary data to be valid only throughout the same roundtrip, use my solution above.

Note

This blog post was created and tested with C4C release 1708.

Thanks to Horst Schaude who had the idea of using the document change time to separate ABSL calls caused by the infinite loop from ABSL calls triggered by actual changes in the UI.

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply