xquery version "3.0";
(:
    ART-DECOR® STANDARD COPYRIGHT AND LICENSE NOTE
    Copyright © ART-DECOR Expert Group and ART-DECOR Open Tools GmbH
    see https://docs.art-decor.org/copyright and https://docs.art-decor.org/licenses

    This file is part of the ART-DECOR® tools suite.
:)

module namespace iss             = "http://art-decor.org/ns/decor/issue";

import module namespace adsearch = "http://art-decor.org/ns/decor/search" at "api-decor-search.xqm";
import module namespace aduser   = "http://art-decor.org/ns/art-decor-users" at "api-user-settings.xqm";
import module namespace get      = "http://art-decor.org/ns/art-decor-settings" at "../modules/art-decor-settings.xqm";
import module namespace art      = "http://art-decor.org/ns/art" at "../modules/art-decor.xqm";
import module namespace templ    = "http://art-decor.org/ns/decor/template" at "../api/api-decor-template.xqm";
import module namespace cs       = "http://art-decor.org/ns/decor/codesystem" at "../api/api-decor-codesystem.xqm";
import module namespace vs       = "http://art-decor.org/ns/decor/valueset" at "../api/api-decor-valueset.xqm";

declare namespace error          = "http://art-decor.org/ns/decor/issue/error";
declare namespace xs             = "http://www.w3.org/2001/XMLSchema";
declare namespace f              = "http://hl7.org/fhir";

(:~
:   All functions support their own override, but this is the fallback for the maximum number of results returned on a search
:)
declare variable $iss:maxResults := 50;

(:~
:   Return zero or more issues as-is
:   
:   @param $id           - required. Identifier of the issue to retrieve
:   @return Matching issues
:   @since 2014-07-09
:)
declare function iss:getIssueById ($id as xs:string) as element(issue)* {
    $get:colDecorData//issue[@id = $id]
};

(:~
:   Return zero or more issues as-is
:   
:   @param $id            - required. Identifier of the object to retrieve the issue for
:   @param $effectiveDate - optional. Effective date of the object to retrieve the issue for
:   @return Matching issues
:   @since 2014-07-09
:)
declare function iss:getIssueByObject ($id as xs:string, $effectiveDate as xs:string?) as element(issue)* {
    if (string-length($effectiveDate)=0) then
        $get:colDecorData//issue[object[@id = $id]]
    else (
        $get:colDecorData//issue[object[@id = $id][@effectiveDate = $effectiveDate]]
    )
};

(:~
:   Return expanded issue metadata on the issue element, and subsequently inside one or more 
:   &lt;object&gt; elements that the issue is connected to.
:   
:   @param $id                      - required. Identifier of the issue to retrieve
:   @param $includeSubscriptionInfo - required. Whether or not to include subscription info the current user
:   @return Matching issues in &lt;return/&gt; element
:   @since 2014-07-09
:)
declare function iss:getIssueMetaById ($id as xs:string, $includeSubscriptionInfo as xs:boolean) as element(issue)* {
let $issues                 := iss:getIssueById($id)

for $issue in $issues
return
    iss:getIssueMeta($issue, $includeSubscriptionInfo)
};

(:~
:   Return expanded issue metadata on the issue element, and subsequently inside one or more 
:   &lt;object&gt; elements that the issue is connected to.
:   
:   @param $issue - required. The original issue
:   @param $includeSubscriptionInfo - required. Whether or not to include subscription info the current user
:   @return Issue with metadata as attributes and objects as-is
:   @since 2014-07-09
:)
declare function iss:getIssueMeta ($issue as element(issue), $includeSubscriptionInfo as xs:boolean) as element(issue) {
let $projectPrefix          := $issue/ancestor::decor/project/@prefix
let $decorAuthors           := art:getDecorByPrefix($projectPrefix)/project/author

return iss:getIssueMeta($issue, $includeSubscriptionInfo, $decorAuthors, $projectPrefix)
};
declare function iss:getIssueMeta ($issue as element(issue), $includeSubscriptionInfo as xs:boolean, $decorAuthors as element(author)*, $projectPrefix as xs:string) as element(issue) {
let $minEffTracking         := min($issue/tracking/xs:dateTime(@effectiveDate))
let $maxEffTracking         := max($issue/tracking/xs:dateTime(@effectiveDate))
let $maxEffAssignment       := max($issue/assignment/xs:dateTime(@effectiveDate))

let $firstTracking          := ($issue/tracking[@effectiveDate = $minEffTracking])[1]
let $lastAssignment         := ($issue/assignment[@effectiveDate = $maxEffAssignment])[1]
let $lastTracking           := ($issue/tracking[@effectiveDate = $maxEffTracking])[1]

let $maxEffAll              := max(($lastTracking|$lastAssignment)/xs:dateTime(@effectiveDate))
let $lastEvent              := $lastTracking[@effectiveDate=$maxEffAll] | $lastAssignment[@effectiveDate=$maxEffAll]
let $lastEvent              := $lastEvent[last()]

let $lastEventAuthorId      := ($lastEvent/author/@id)[1]
let $lastEventAuthorName    := $decorAuthors[@id=$lastEventAuthorId]
let $lastEventAuthorName    := 
    if ($lastEventAuthorName) then
        $lastEventAuthorName
    else
        ($lastEvent/author)[1]/text()

let $currentType            := $issue/@type
let $currentPriority        := if ($issue/@priority[not(.='')]) then ($issue/@priority) else ('N')
let $currentStatus          := $lastTracking/@statusCode
let $currentLabels          := $lastEvent/@labels

let $issueAuthorId          := ($firstTracking/author/@id)[1]
let $issueAuthorUserName    := $decorAuthors[@id = $issueAuthorId]/@username
let $issueAssignedId        := $lastAssignment/@to
let $issueAssignedAuthor    := $decorAuthors[@id = $issueAssignedId]
let $issueAssignedUserName  := $issueAssignedAuthor/@username
let $issueAssignedName      := 
    if ($issueAssignedAuthor) then
        $issueAssignedAuthor/text()
    else (
        $lastAssignment/@name
    )
let $currentUserIsSubscribed    := 
    if ($includeSubscriptionInfo) then 
        attribute currentUserIsSubscribed {aduser:userHasIssueSubscription($issue/@id)} 
    else ()
let $currentUserAutoSubscribes  := 
    if ($includeSubscriptionInfo) then 
        attribute currentUserAutoSubscribes {aduser:userHasIssueAutoSubscription($projectPrefix, $issue/@id, $issue/object/@type, $issueAuthorUserName[1], $issueAuthorUserName[1])} 
    else ()
return
    <issue  id="{$issue/@id}" 
            priority="{$currentPriority}" 
            displayName="{$issue/@displayName}" 
            type="{$currentType}" 
            currentStatusCode="{$currentStatus}"
            currentLabels="{$currentLabels}" 
            lastDate="{$lastEvent/@effectiveDate}" 
            lastAuthorId="{$lastEventAuthorId}"
            lastAuthor="{$lastEventAuthorName}" 
            lastAssignmentId="{$issueAssignedId}"
            lastAssignment="{$issueAssignedName}"
            iddisplay="{art:getNameForOID($issue/@id, (), $issue/ancestor::decor)}"
            ident="{$issue/ancestor::decor/project/@prefix}">
    {
        $currentUserIsSubscribed,
        $currentUserAutoSubscribes
    }
    {
        $issue/object
    }
    </issue>
};

(:~
:   Return zero or more expanded issue metadata wrapped in a &lt;return/&gt; element, and subsequently inside one or more &lt;template&gt; elements for
:   server local repositories that aren't private and either refer to or define the requested template(s).
:   See templ:getTemplateById ($id as xs:string, $flexibility as xs:string?, $prefix as xs:string, $version as xs:string?) for more info
:   
:   @param $prefix       - required. DECOR project/@prefix to get issues from
:   @param $searchTerms  - optional. List of terms that results should match in issue/@displayName or any desc element. Results all if empty
:   @param $types        - optional. List of issue/@type values that results should have. Returns all if empty
:   @param $priorities   - optional. List of issue/@priority values that results should have. Returns all if empty
:   @param $statuscodes  - optional. List of issue most recent status code values that results should have. Returns all if empty
:   @param $lastassignedids - optional. List of project/author/@id values that the latest assigned person of the issue should match. Returns all if empty
:   @param $labels       - optional. List of issue most recent label code values that results should have. Returns all if empty
:   @param $changedafter - optional. List issues that where changed on or after this value. Acceptable values: YYYY(-MM(-SST(hh(\:mm(\:ss([+/-]ZZ:((zz))))))) and T(([+/-]\d+(\.\d+)?[YMDhms])?{{hh:mm:ss}}?)?
:   @param $sort         - optional. Value to sort on. Supported are 'issue' (display name),'priority','type','status','date','assigned-to','label'. There's no default.
:   @param $max          - optional. Maximum number of results with minimum 1. Default is $iss:maxResults (50)
:   @return Matching issues in &lt;return/&gt; element
:   @since 2014-07-09
:)
declare function iss:getIssueList ($prefix as xs:string, $searchTerms as xs:string*, $types as xs:string*, $priorities as xs:string*, $statuscodes as xs:string*, $lastassignedids as xs:string*, $labels as xs:string*, $sort as xs:string?, $max as xs:integer?) as element(return) {
    iss:getIssueList($prefix, $searchTerms, $types, $priorities, $statuscodes, $lastassignedids, $labels, (), $sort, $max)
};
declare function iss:getIssueList ($prefix as xs:string, $searchTerms as xs:string*, $types as xs:string*, $priorities as xs:string*, $statuscodes as xs:string*, $lastassignedids as xs:string*, $labels as xs:string*, $changedafter as xs:string?, $sort as xs:string?, $max as xs:integer?) as element(return) {
let $max            := if ($max >= 1) then $max else $iss:maxResults
let $decor          := art:getDecorByPrefix($prefix)
let $projectAuthors := $decor/project/author
let $issues         := $decor/issues/issue
let $allcnt         := count($issues)
let $termIsId       := count($searchTerms)=1 and $searchTerms[1] castable as xs:integer
let $changedafter   := art:parseAsDateTime($changedafter)

let $issues         := 
    if ($termIsId) then
        $issues[ends-with(@id,concat('.',$searchTerms[1]))]
    else (
        let $r  := 
            if (empty($searchTerms)) then $issues else (
                let $luceneQuery    := adsearch:getSimpleLuceneQuery($searchTerms)
                let $luceneOptions  := adsearch:getSimpleLuceneOptions()
                return 
                    $issues[ft:query(@displayName|*/desc,$luceneQuery,$luceneOptions)]
            )
        let $r  := if (empty($types))           then $r else ($r[@type=$types])
        let $r  := if (empty($priorities))      then $r else ($r[@priority=$priorities])
        let $r  := if (empty($changedafter))    then $r else ($r[tracking[xs:dateTime(@effectiveDate) ge $changedafter] | assignment[xs:dateTime(@effectiveDate) ge $changedafter]])
        let $r  := if (empty($statuscodes))     then $r else (for $issue in $r return $issue[*[@statusCode][last()]/@statusCode=$statuscodes])
        let $r  := if (empty($lastassignedids)) then $r else if ($lastassignedids='#UNASSIGNED#') then $r[not(assignment)] else (for $issue in $r return $issue[*[@to][last()]/@to=$lastassignedids])
        let $r  := if (empty($labels))          then $r else (for $issue in $r return $issue[(tracking | assignment)[last()]/tokenize(@labels, '\s')=$labels])
        
        return $r
    )

(:can sort on indexed db content -- faster:)
let $results    :=
    switch ($sort) 
    case 'issue'        return for $issue in $issues order by $issue/@displayName return $issue
    case 'priority'     return for $issue in $issues order by $issue/@priority return $issue
    case 'type'         return for $issue in $issues order by $issue/@type return $issue
    default             return $issues

(:calculate missing meta data:)
let $results    :=
    for $issue in $results
    return
        iss:getIssueMeta($issue, false(), $projectAuthors, $prefix)

(:can sort on calculated content -- slower:)
let $results    :=
    switch ($sort) 
    case 'status'       return for $issue in $results order by $issue/@currentStatusCode return $issue
    case 'date'         return for $issue in $results order by $issue/@lastDate descending return $issue
    case 'assigned-to'  return for $issue in $results order by $issue/@lastAssignment return $issue
    case 'label'        return for $issue in $results order by $issue/@currentLabels return $issue
    (:default would mean id or something we don't support, but that would be impossible at this point:)
    default             return $results

let $count := count($results)

(:<assignee to="19" name="Alexander Henket (AH)" effectiveDate="2015-05-27T11:30:33"/>:)
return
    <return current="{if ($count<=$max) then $count else $max}" total="{$count}" all="{$allcnt}">
        <meta>
        {
            (:other meta could be useful later, but not used for now so don't let it eat performance:)
            (:for $t in $issues/@type
            let $l := $t
            group by $l
            return
            <type>{$t[1]}</type>
            ,
            for $t in $issues/@priority
            let $l := $t
            group by $l
            return
            <priority>{$t[1]}</priority>
            ,
            for $t in $issues/*/tokenize(@labels,'\s')
            let $l := $t
            group by $l
            return
            <label label="{$t[1]}"/>
            ,
            for $t in $issues/*/@statusCode
            let $l := $t
            group by $l
            return 
            <statusCode>{$t[1]}</statusCode>
            ,:)
            for $t in $results[@lastAssignmentId[not(. = '')]]
            let  $l := $t/@lastAssignmentId
            group by $l
            order by $projectAuthors[@id=$t[1]/@lastAssignmentId][1]
            return
            <assignee to="{$l}" name="{$t[1]/@lastAssignment}" count="{count($t)}"/>
        }
        </meta>
    {
        subsequence($results,1,$max)
    }
    </return>
};

(:~
:   Return zero or more expanded issues wrapped in a &lt;return/&gt; element
:   
:   @param $ids           - optional. Identifier of the template to retrieve
:   @return Matching issues in &lt;return/&gt; element
:   @since 2014-07-09
:)
declare function iss:getExpandedIssuesById ($ids as item()*, $skipObjects as xs:boolean) as element(return) {
let $issues                 := 
    for $id in $ids 
    return 
        if ($id instance of element(issue)) then $id else iss:getIssueById($id)

return
    <return>
    {
        for $issue in $issues
        let $decor                  := $issue/ancestor::decor
        let $projectPrefix          := $decor/project/@prefix
        let $projectVersion         := $decor/@versionDate
        let $language               := ($decor/@language[not(. = ('*', ''))], $decor/project/@defaultLanguage)[1]
        let $objectmap  :=
            map:merge( 
                for $o in $issue/object[not($skipObjects)]
                let $obid    := $o/@id
                let $obed    := $o/@effectiveDate[. castable as xs:dateTime]
                group by $obid, $obed
                return map:entry(concat($obid, $obed), ($o[1]/@id, $o[1]/@type, $o[1]/@effectiveDate[. castable as xs:dateTime])) 
            )
        return
            <issue>
            {
                iss:getIssueMeta($issue, true())/@*
            }
            {
                (: copy label definition to make labels displayable outside of their project e.g. based on the object :)
                $issue/ancestor::issues/labels[label]
            }
            {
                for $key in map:keys($objectmap)
                let $te             := map:get($objectmap, $key)
                let $objectId       := $te[1]
                let $objectType     := $te[2]
                let $objectEffDate  := $te[3]
                let $objectContent  := 
                    (: optimization. if we let it search without being specific, then performance decreases drastically :)
                    if ($objectType = 'VS') then (
                        (:get valueset:)
                        let $valueSet   := vs:getExpandedValueSetById($objectId, $objectEffDate, $projectPrefix, $projectVersion, $language, true())/*/valueSet
                        return 
                            if ($valueSet[@id]) then (<valueSet>{$valueSet[@id][1]/(@* except (@url|@ident)), $valueSet[@id][1]/parent::*/(@url|@ident), $valueSet[@id][1]/node()}</valueSet>) else ($valueSet[1])
                    ) else
                    if ($objectType = 'DE') then (
                        (:get data element (dataset concept):)
                        art:getConcept($objectId, $objectEffDate, $projectVersion, $language)
                    ) else 
                    if ($objectType = 'TM') then (
                        (:get template:)
                        let $templates  := templ:getTemplateById($objectId, $objectEffDate, $projectPrefix, $projectVersion)//template[@id][@effectiveDate]
                        return 
                            if ($templates) then (<template>{$templates[1]/@*}</template>) else ()
                    ) else 
                    if ($objectType = 'EL') then (
                        (:get template element:)
                        let $o  := $decor//element[@id = $objectId]
                        return $o[ancestor::template]
                    ) else 
                    if ($objectType = 'TR') then (
                        (:get transaction:)
                        art:getTransaction($objectId, $objectEffDate)
                    ) else 
                    if ($objectType = 'DS') then (
                        (:get dataset:)
                        art:getDataset($objectId, $objectEffDate)
                    ) else 
                    if ($objectType = 'SC') then (
                        (:get scenario:)
                        art:getScenario($objectId, $objectEffDate)
                    ) else 
                    if ($objectType = 'CS') then (
                        (:get codeSystem:)
                        cs:getCodeSystemById($objectId, $objectEffDate)//codeSystem
                    ) else 
                    if ($objectType = 'IS') then (
                        (:get issue:)
                        iss:getIssueById($objectId)
                    ) else 
                    if ($objectType = 'QQ' or $objectType = 'QE') then (
                        (:get questionnaire:)
                        $get:colDecorData//questionnaire[@id = $objectId][@effectiveDate  = $objectEffDate] | $get:colDecorCache//questionnaire[@id = $objectId][@effectiveDate  = $objectEffDate]
                    ) else
                    if ($objectType = 'QR') then (
                        (:get questionnaireresponse (FHIR) :)
                        $get:colDecorData//f:QuestionnaireResponse[f:id/@value = $objectId]
                    ) else
                    if (empty($objectEffDate)) then (
                        (:get any -- performance hit!:)
                        $decor//*[@id = $objectId][not(ancestor::history | self::object)]
                    ) 
                    else (
                        (:get any -- performance hit!:)
                        let $o  := $decor//*[@id = $objectId]
                        return $o[@effectiveDate = $objectEffDate][not(ancestor::history | self::object)]
                    )
                let $objectContent := $objectContent[1]
                order by $objectType, $objectId, $objectEffDate
                return
                    <object id="{$objectId}">
                    {
                        if (empty($objectEffDate)) then () else attribute effectiveDate {$objectEffDate},
                        if (empty($objectType))    then () else attribute type {$objectType}
                    }
                    {
                        if ($objectType = 'DE') then (
                            let $datasetContent     := $objectContent/ancestor::dataset
                            (:usually you inherit from the original, but if you inherit from something that inherits, then these two will differ
                                originalConcept has the type and the associations. The rest comes from the inheritConcept
                            :)
                            let $inheritConcept     := if ($objectContent/inherit) then art:getConcept($objectContent/inherit/@ref, $objectContent/inherit/@effectiveDate) else ()
                            let $originalConcept    := art:getOriginalForConcept($objectContent)
                            return (
                                $objectContent/(@* except (@id|@type|@effectiveDate|@name|@linkedartefactmissing|@iddisplay)),
                                attribute name {($originalConcept/name[@language = $language], $originalConcept/name)[1]},
                                attribute iddisplay {art:getNameForOID($objectId, $language, $originalConcept/ancestor::decor)},
                                attribute linkedartefactmissing { empty($objectContent) }
                                ,
                                <dataset>
                                {
                                    $datasetContent/@*,
                                    attribute iddisplay {art:getNameForOID($datasetContent/@id, $language, $objectContent/ancestor::decor)},
                                    $datasetContent/name,
                                    <path>
                                    {
                                    for $ancestor in $objectContent/ancestor::concept
                                    let $c  := art:getOriginalForConcept($ancestor)
                                    return
                                        concat($c/name[@language = $language][1]/text(), ' / ')
                                    }
                                    </path>
                                }
                                </dataset>
                                ,
                                if ($objectContent/inherit) then (
                                    <inherit>
                                    {
                                        $objectContent/inherit/(@ref|@effectiveDate),
                                        $originalConcept/ancestor::decor/project/@prefix,
                                        attribute datasetId {$inheritConcept/ancestor::dataset/@id},
                                        attribute datasetEffectiveDate {$inheritConcept/ancestor::dataset/@effectiveDate},
                                        attribute iType {$originalConcept/@type}, 
                                        attribute iStatusCode {$inheritConcept/@statusCode}, 
                                        attribute iEffectiveDate {$inheritConcept/@effectiveDate},
                                        attribute iExpirationDate {$inheritConcept/@expirationDate},
                                        attribute iVersionLabel {$inheritConcept/@versionLabel},
                                        attribute iddisplay {art:getNameForOID($objectContent/inherit/@ref, $objectContent/ancestor::decor/project/@defaultLanguage, $objectContent/ancestor::decor)}, 
                                        attribute localInherit {$originalConcept/ancestor::decor/project/@prefix=$projectPrefix},
                                        if ($inheritConcept[@id = $originalConcept/@id][@effectiveDate = $originalConcept/@effectiveDate]) then () else (
                                            attribute originalId {$originalConcept/@id}, 
                                            attribute originalEffectiveDate {$originalConcept/@effectiveDate}, 
                                            attribute originalStatusCode {$originalConcept/@statusCode}, 
                                            attribute originalExpirationDate {$originalConcept/@expirationDate},
                                            attribute originalVersionLabel {$originalConcept/@versionLabel}
                                        )
                                    }
                                    </inherit>
                                ) else ()
                                ,
                                for $node in $originalConcept/(name|synonym|desc|source|rationale)
                                return art:serializeNode($node)
                                ,
                                for $node in $objectContent/comment
                                return art:serializeNode($node)
                                ,
                                if ($objectContent/inherit) then (
                                    for $node in $originalConcept/comment
                                    return
                                        <inheritedComment language="{$node/@language}">{art:serializeNode($node)/text()}</inheritedComment>
                                ) else ()
                                ,
                                (:new since 2015-04-21:)
                                for $node in $originalConcept/property
                                return art:serializeNode($node)
                                ,
                                (:new since 2015-04-21:)
                                for $relationship in $originalConcept/relationship
                                let $referredConcept    := art:getOriginalForConcept(<concept><inherit ref="{$relationship/@ref}" effectiveDate="{$relationship/@flexibility}"/></concept>)[1]
                                return
                                    <relationship type="{$relationship/@type}" ref="{$relationship/@ref}" flexibility="{$relationship/@flexibility}">
                                    {
                                        $referredConcept/ancestor::decor/project/@prefix,
                                        attribute datasetId {$referredConcept/ancestor::dataset/@id},
                                        attribute datasetEffectiveDate {$referredConcept/ancestor::dataset/@effectiveDate},
                                        attribute iType {$referredConcept/@type}, 
                                        attribute iStatusCode {$referredConcept/@statusCode}, 
                                        attribute iExpirationDate {$referredConcept/@expirationDate},
                                        attribute iVersionLabel {$referredConcept/@versionLabel},
                                        attribute iddisplay {art:getNameForOID($relationship/@ref, $language, $referredConcept/ancestor::decor)},
                                        attribute localInherit {$referredConcept/ancestor::decor/project/@prefix = $projectPrefix},
                                        $referredConcept/name,
                                        if ($referredConcept/name[@language=$language]) then () else (
                                            <name language="{$language}">{data($referredConcept/name[1])}</name>
                                        )
                                    }
                                    </relationship>
                                ,
                                for $node in $originalConcept/operationalization
                                return art:serializeNode($node)
                                ,
                                for $valueDomain in $originalConcept/valueDomain
                                return
                                    <valueDomain>
                                    {
                                        $valueDomain/@*,
                                        $valueDomain/property[@*[string-length()>0]],
                                        for $conceptList in $valueDomain/conceptList 
                                        let $originalConceptList := art:getOriginalConceptList($conceptList)
                                        return
                                            <conceptList>
                                            {
                                                (:note that this will retain conceptList[@ref] if applicable:)
                                                $conceptList/(@* except @conceptId),
                                                if ($conceptList[@ref]) then (
                                                    attribute prefix {$originalConceptList/ancestor::decor/project/@prefix},
                                                    attribute conceptId {$originalConceptList/ancestor::concept[1]/@id},
                                                    attribute conceptEffectiveDate {$originalConceptList/ancestor::concept[1]/@effectiveDate},
                                                    attribute datasetId {$originalConceptList/ancestor::dataset[1]/@id},
                                                    attribute datasetEffectiveDate {$originalConceptList/ancestor::dataset[1]/@effectiveDate}
                                                )
                                                else (),
                                                for $conceptListNode in $originalConceptList/*
                                                return
                                                    if ($conceptListNode/self::concept) then (
                                                        <concept>
                                                        {
                                                            $conceptListNode/@*,
                                                            $conceptListNode/name,
                                                            $conceptListNode/synonym,
                                                            for $node in $conceptListNode/desc
                                                            return art:serializeNode($node)
                                                        }
                                                        </concept>
                                                    )
                                                    else (
                                                        $conceptListNode
                                                    )
                                            }
                                            </conceptList>
                                        ,
                                        $valueDomain/example
                                    }
                                    </valueDomain>
                                (:,
                                for $inhC in $inheritedConcept/concept
                                return
                                <concept>{$inhC/@*,$inhC/*}</concept>:)
                            )
                        ) else 
                        if ($objectType = 'VS') then (
                            $objectContent/(@* except (@id|@type|@effectiveDate|@linkedartefactmissing|@iddisplay)),
                            attribute iddisplay {art:getNameForOID($objectId, $language, $objectContent/ancestor::decor)},
                            attribute linkedartefactmissing { empty($objectContent) }
                            ,
                            $objectContent/*
                        )
                        else 
                        if ($objectType = 'TM') then (
                            $objectContent/(@* except (@id|@type|@effectiveDate|@linkedartefactmissing|@iddisplay)),
                            attribute iddisplay {art:getNameForOID($objectId, $language, $objectContent/ancestor::decor)},
                            attribute linkedartefactmissing { empty($objectContent) }
                            ,
                            $objectContent
                            
                        ) else 
                        if ($objectType = 'TR') then (
                            let $datasetId  := $objectContent/representingTemplate/@sourceDataset[not(. = '')]
                            let $datasetEd  := $objectContent/representingTemplate/@sourceDatasetFlexibility[. castable as xs:dateTime]
                            let $datasets   := 
                                if ($datasetId) then 
                                    art:getDataset($datasetId, $datasetEd, $projectVersion, $language)
                                else ()
                            let $templateId := $objectContent/representingTemplate/@ref[not(. = '')]
                            let $templateEd := $objectContent/representingTemplate/@flexibility[. castable as xs:dateTime]
                            let $templates  := 
                                if ($templateId) then 
                                    templ:getTemplateById($templateId, $templateEd, $projectPrefix, $projectVersion)//template[@id][@effectiveDate]
                                else ()
                            return (
                                $objectContent/(@* except (@id|@type|@effectiveDate|@linkedartefactmissing|@iddisplay)),
                                attribute iddisplay {art:getNameForOID($objectId, $language, $objectContent/ancestor::decor)},
                                attribute linkedartefactmissing { empty($objectContent) }
                                ,
                                $objectContent/(* except (representingTemplate|transaction)),
                                if ($objectContent/representingTemplate) then (
                                    <representingTemplate>
                                    {
                                        $objectContent/representingTemplate/@*,
                                        if ($templates) then (
                                            attribute {'templateName'} {($templates[1]/@displayName, $templates[1]/@name)[1]}
                                        ) else (),
                                        if ($datasets) then (
                                            attribute {'datasetName'} {($datasets/name[@language = $language], $datasets/name)[1]}
                                        ) else ()
                                    }
                                    </representingTemplate>
                                ) else ()
                            )
                         ) else (
                            $objectContent/(@* except (@id|@type|@effectiveDate|@linkedartefactmissing|@iddisplay|@statusCode)),
                            attribute statusCode {$objectContent/@statusCode | $objectContent//tracking[@effectiveDate=max($objectContent//tracking/xs:dateTime(@effectiveDate))][1]/@statusCode | $objectContent/f:status/@value},
                            attribute iddisplay {art:getNameForOID($objectId, $language, $objectContent/ancestor::decor)},
                            attribute linkedartefactmissing { empty($objectContent) }
                            ,
                            $objectContent/name,
                            for $nm in $objectContent/f:questionnaire/@value
                            return
                                <name language="{$language}">QuestionnaireResponse for {data($nm)}</name>
                            ,
                            $objectContent/desc
                        )
                    }
                    </object>
            }
            {
                for $event in $issue/tracking|$issue/assignment
                order by xs:dateTime($event/@effectiveDate) descending
                return
                if ($event[self::tracking]) then
                    <tracking effectiveDate="{$event/@effectiveDate}" statusCode="{$event/@statusCode}">
                    {
                        if (string-length($event/@labels)>0) then ($event/@labels) else (),
                        for $author in $event/author
                        return
                        <author>
                        {
                            $author/@*[string-length(.)>0],
                            if (string-length(string-join($author/text(),''))>0) then (
                                $author/text()
                            ) else (
                                $decor//project/author[@id=$author/@id]/text()
                            )
                        }
                        </author>
                    }
                    {
                        for $desc in $event/desc
                        return
                        art:serializeNode($desc)
                    }
                    </tracking>
                else if ($event[self::assignment]) then
                    <assignment to="{$event/@to}" name="{$event/@name}" effectiveDate="{$event/@effectiveDate}">
                    {
                        if (string-length($event/@labels)>0) then ($event/@labels) else (),
                        for $author in $event/author
                        return
                        <author>
                        {
                            $author/@*[string-length(.)>0],
                            if (string-length(string-join($author/text(),''))>0) then (
                                $author/text()
                            ) else (
                                $decor//project/author[@id=$author/@id]/text()
                            )
                        }
                        </author>
                    }
                    {
                        for $desc in $event/desc
                        return
                        art:serializeNode($desc)
                    }
                    </assignment>
                else()
            }
            </issue>
    }
    </return>
};

(:~
:   Return zero or more expanded issues wrapped in a &lt;return/&gt; element
:   
:   @param $id            - required. Identifier of the object to retrieve the issue for
:   @param $effectiveDate - optional. Effective date of the object to retrieve the issue for
:   @return Matching issues
:   @since 2014-07-09
:   @history 2014-11-21 AH Added return/@id and return/@effectiveDate for efficiency on caller 
:       so he can match his current context against what has come back in asynchronous mode
:)
declare function iss:getExpandedIssuesByObject ($id as xs:string, $effectiveDate as xs:string?) as element(return) {
<return id="{$id}" effectiveDate="{$effectiveDate}">
{
    iss:getExpandedIssuesById(iss:getIssueByObject($id, $effectiveDate), true())/issue
}
</return>
};
