Transition Condition for Checking a Hard Point of No Return

The following XQuery can be used to check whether a hard point of no return has been reached, so that an amendment can be rejected if it is received after a hard point of no return. This XQuery checks to see whether there have been any revisions to significant data for order items that have reached a hard point of no return. Business considerations will determine what state/transition combinations will need to check for the point of no return, but at a minimum it should be defined in the In Progress state for the Submit Amendment transition.

To use this XQuery, follow the standard procedure for updating the lifecycle policy, creating a new transition condition and using the XQuery below in the Expression box for that condition. See "Configuring Order Lifecycle Policies" for more information about updating the lifecycle policy.

declare variable $PONR_NOT_YET := "NOT YET";

(: Checks for Hard Point Of No Return, return = true means no PONR 
   has been reached. Raise an error if PONR has been reached. :)
declare function local:allowRevision(
    $taskData as element()) as xs:boolean {
    let $rootData := $taskData/_root
    let $changes := $taskData/RevisionPerspective/Changes
    return
        if (fn:exists($rootData) and fn:exists($changes))
        then (
            let $changedOrderItems as element()* := 
                    local:getChangedOrderItems($rootData, $changes)
            let $revisionOrderItemsPastHardPONR as xs:string* :=
                for $orderItem in $changedOrderItems
                    return local:getOrderItemsPastHardPONR($orderItem)
            return fn:not(fn:exists($revisionOrderItemsPastHardPONR)))
        else fn:true() };

declare function local:getChangedOrderItems(
    $root as element(), 
    $changes as element()) as element()* {
    let $indices := local:getOrderItemIndicesForChecking($changes)
    let $distinctIndicies := fn:distinct-values($indices)
    for $index in $distinctIndicies
        return local:getOrderItem($root, $index) };

declare function local:getOrderItemIndicesForChecking(
    $changes as element()) as xs:string* {
    for $change in $changes/*[@significant = "true"]
        return local:getOrderItemIndex($change) };

declare function local:getOrderItemIndex(
    $changeNode as element()) as xs:string* {
    let $changeType := local-name($changeNode)
    let $tokens := fn:tokenize($changeNode/@path, "/")
    let $t1 := $tokens[position() = 2]
    let $t2 := $tokens[position() = 3]
    let $t3 := $tokens[position() = 4]
    let $t4 := $tokens[position() = 5]
    return
        if (fn:starts-with($t1, "ControlData") 
            and fn:starts-with($t2, "Functions")) then
            (: /ControlData/Functions/*Function/orderItem/... :)
            local:getOrderItemIndexInFunction(
                fn:root($changeNode)/GetOrder.Response/_root,
                (: Functions/@index, if exists :)
                fn:substring-before(fn:substring-after($t2,"'"), "'"),
                (: e.g. SyncCustomerFunction/@index :)
                fn:substring-before(fn:substring-after($t3,"'"), "'"),
                (: e.g. orderItem/@index :)
                fn:substring-before(fn:substring-after($t4,"'"), "'"))
        else
                ""   };

declare function local:getOrderItemIndexInFunction(
    $root as element(),
    $functionsIndex as xs:string,
    $functionIndex as xs:string,
    $orderItemIndex as xs:string) as xs:string* {
    if (fn:boolean($functionsIndex)) then 
        $root/ControlData/Functions[@index = $functionsIndex]/*[@index = 
            $functionIndex]/orderItem[@index = 
            $orderItemIndex]/orderItemRef/@referencedIndex
    else
        $root/ControlData/Functions/*[@index = $functionIndex]/orderItem[@index =
            $orderItemIndex]/orderItemRef/@referencedIndex    };

declare function local:getOrderItem(
    $root as element(), 
    $orderItemIndex as xs:string) as element()* {
    $root/ControlData/OrderItem[@index = $orderItemIndex]    };

declare function local:getOrderItemsPastHardPONR(
    $orderItem as element()) as xs:string* {
    let $lineId as xs:string := local:getLineId($orderItem)
    let $pointOfNoReturn as xs:string := local:getPointOfNoReturn($orderItem)
    let $isHardPONRReached := if ($pointOfNoReturn = "HARD") 
                                 then true() 
                                 else false()
    return
        if ($isHardPONRReached)
        then $lineId
        else () };

declare function local:getLineId(
    $orderItem as element()) as xs:string {
    fn:normalize-space($orderItem/LineID/text()) };

declare function local:getPointOfNoReturn(
    $orderItem as element()) as xs:string {
    let $ponrData := fn:normalize-space($orderItem/PoNR/text())
    let $ponrCode :=
        if (fn:empty($ponrData))  
        then $PONR_NOT_YET
        else (
            let $lastPonrValue := 
                fn:normalize-space($orderItem/PoNR[last()]/text())
            return
                (: We are looking for strings with either [xxxx]xxxx or 
                   xxxx format. Return what is in the [] or the whole string 
                   if no brackets. :)
                let $hard1 := fn:tokenize($lastPonrValue, "\[|\]") 
                return fn:concat( $hard1[1] , $hard1[2] )
        )
    return
        $ponrCode   };

(: Detect false revision order. return = true means 
   there are significant data changes in the revision order :)
declare function local:doSignificantChangesExist(
    $taskData as element()) as xs:boolean {
    let $dataChanges := 
        $taskData/RevisionPerspective/Changes/*[@significant='true']
    return
        if (fn:exists($dataChanges))
        then true()
        else false() };

(: Only do the complex calculation for a valid revision.:)
let $taskData := fn:root(.)/GetOrder.Response
let $isValidRevision := local:doSignificantChangesExist($taskData)
return if ($isValidRevision) 
then
    local:allowRevision($taskData)
else
    fn:true()