@Eduard Lebedyuk précise :

L'idée d'un ensemble de résultats défilants est d'appeler Save/OpenId - et l'ensemble de résultats continuera automatiquement sur une ligne suivante. Vous n'avez donc pas besoin de gérer les indices avant/arrière :

 
Un exemple ici

C'est également environ 3 fois plus rapide puisque la requête n'est exécutée qu'une seule fois : 

do ##class(User.Pagination).Time("Save")
Save took 0,0048 sec
do ##class(User.Pagination).Time("NoSave")
NoSave took 0,0143 sec

@Guillaume Rongier ajoute :

Bonjour Rubén,

Une autre proposition sur IRIS 2021.1+ peut être celle-ci avec l'utilisation de la fonction nouvelle fenêtre (OVER) :

ClassMethod getPersonsPagWindow(iAge As %Integer, sortField As %String = 1, sortOrder As %String = 2, pageSize As %String = 20, pageIndex As %String = 1) As %DynamicObject
{
    set out = []
    set vFrom = ((pageIndex -1 ) * pageSize)+1
    set vTo = vFrom + (pageSize-1)

    set sql = "SELECT * "_
                "FROM ( SELECT persons.* "_
                "        , ROW_NUMBER() OVER (ORDER By "_sortField_" "_ $CASE(sortOrder,1:"ASC",2:"DESC",:"ASC")_
                "    ) rn "_
                "        FROM Sample.Person persons where Age > ? "_
                "    ) tmp "_
                "WHERE rn between "_vFrom_" and "_vTo_" "_
                "ORDER By "_sortField_" "_ $CASE(sortOrder,1:"ASC",2:"DESC",:"ASC")

    Set rs=##class(%ResultSet).%New("%DynamicQuery:SQL")
    set sc = rs.Prepare(sql)
    set sc = rs.Execute(iAge) If $$$ISERR(sc) Do DisplayError^%apiOBJ(sc) Quit

    while rs.%Next() {
        Do out.%Push({
                "pid": (rs.%Get("ID")),
                "ssn" : (rs.%Get("SSN")),
                "lastname" : (rs.%Get("LastName")) ,
                "givenname":    (rs.%Get("GivenName")),
                "secondaryname":       (rs.%Get("SecondaryName")) ,
                "gender": (rs.%Get("Gender")),
                "age": (rs.%Get("Age") )
                })
    }

    set outJson = []
    Do outJson.%Push({
                "pageSize":(pageSize),
                "pageIndex":(pageIndex),
                "fromIndex":(vFrom),
                "toIndex":(vTo),
                "resultSet":(out)
                })
    return outJson
}

Je compare les deux solutions sur un dataset de 100 000 lignes sans index avec un résultat de 20 éléments en page 1 et voici les résultats :

"getPersonsPag timed : 1,647 secondes"
"getPersonsPagWindow timed : 0,247 secondes"

Je suppose que la fonction window est plus rapide car vous n'avez pas besoin de récupérer toutes les données de manière globale avant la pagination.

Une fois obtenu le fichier Excel, il est possible de le convertir en CSV puis en PDF en utilisant les librairies pandas et csv2pdf

ClassMethod toPDF(file As %String) As %Status [ Language = python ]
{
  import pandas as pd
  from csv2pdf import convert
  PATH = '/home/irisowner/dev/data/'
  PATH_TO_XLSX = PATH+file+'.xlsx'
  PATH_TO_CSV = PATH+file+'.csv'
  PATH_TO_PDF = PATH+file+'.pdf'
  
  read_file = pd.read_excel (PATH_TO_XLSX)
  read_file.to_csv (PATH_TO_CSV, index = None, header=True)
  convert(PATH_TO_CSV , PATH_TO_PDF)
}

Bonjour @Pierre LaFay 
en m'inspirant des liens proposés par @Guillaume Rongier tu trouveras ci-dessous et en ligne un exemple de génération de feuille Excel à partir de openpyxl 

 
Code
 
Exemple de classe utilisant un index IDKEY
 
Test

Réponse de @Ashok Kumar 

Bonjour @Muhammad Waseem 

Vous pouvez peut-être essayer d'extraire la ressource patient du bundle ou l'utiliser si c'est déjà une ressource. Chargez la classe du modèle patient FHIR HS.FHIR.DTL.vR4.Model.Resource.Patient avec la ressource extraite. Ensuite, vous pouvez convertir la date de naissance et valider l'âge. Vous pouvez utiliser la même logique dans votre code DTL dans un processus FHIR entre le service FHIR (HS.FHIRServer.Interop.Service) et l'opération FHIR (HS.FHIRServer.Interop.Operation).

ClassMethod VaidatePatientResource(patientResourceStreram As %Stream.Object)
{
	#dim patient As HS.FHIR.DTL.vR4.Model.Resource.Patient
	try {
		set patient = ##class(HS.FHIR.DTL.vR4.Model.Resource.Patient).FromJSON(patientResourceStreram)
		Set age = $horolog - $ZdateH(patient.birthDate,3)\360
		if age<18 $$$ThrowStatus($$$ERROR($$$GeneralError, "Age is less than 18"))
	}
	catch ex {
		w ex.DisplayString()
	}
}

Bonjour @Julia Pertin 
peux-tu me donner plus de détails sur l'erreur rencontrée ?

Vérifies-bien si tu recrées une production from scratch, en ajoutant service-process-operation, de conserver les noms les BS-BP-BO identiques aux noms des 3 classes EP.service.replication, EP.process.replication et EP.operation.replication, et qu'ils sont bien reliés entre eux.
 

Le code de la Business Opération

Class EP.operation.replication Extends Ens.BusinessOperation
{

Property Adapter As Ens.OutboundAdapter;

Parameter ADAPTER = "Ens.OutboundAdapter";

Parameter INVOCATION = "Queue";

Method sync(pRequest As Ens.StreamContainer, Output pResponse As Ens.StringContainer) As %Status
{
    set pResponse = ##class(Ens.StringContainer).%New()
    set json = [].%FromJSON(pRequest.Stream)
    set array = json.%GetIterator()
    while array.%GetNext(.key,.value) {
        $$$TRACE("Data:"_value.%ToJSON())
        set a = ##class(data.client2).%New()
        set sc = a.%JSONImport(value)
        if 'sc {
            $$$LOGERROR("ERROR while importing:"_$system.Status.GetErrorText(sc))
        } else {
            set sc = a.%Save()
        }
    }
    set pResponse.StringValue = key _" records inserted"
    return sc
}

XData MessageMap
{
<MapItems>
    <MapItem MessageType="Ens.StreamContainer">
        <Method>sync</Method>
    </MapItem>
</MapItems>
}

}

Le code du Business Process

Class EP.process.replication Extends Ens.BusinessProcess [ ClassType = persistent, ProcedureBlock ]
{

Property Server As %String [ InitialExpression = "host.docker.internal" ];

Property Port As %Integer [ InitialExpression = 12773 ];

Property classname As %String [ InitialExpression = "data.client" ];

Parameter SETTINGS = "Server:target,Port:target,classname:target";

Method OnRequest(pRequest As Ens.Request, Output pResponse As Ens.StringContainer) As %Status
{
    set sc = $$$OK
    set tHttpRequest             = ##class(%Net.HttpRequest).%New()
    set pContainer               = ##class(Ens.StreamContainer).%New()
    set stream                   = ##class(%Stream.GlobalCharacter).%New()
    set tHttpRequest.Server      = ..Server
    set tHttpRequest.Port        = ..Port
    set location                 = "/common/list/" _ ..classname _ "/json"

    set tHttpRequest.ContentType = "application/json"
    set tHttpRequest.Timeout     = 900
    
    set tSC = tHttpRequest.Get(location)
  
   if $ISOBJECT(tHttpRequest.HttpResponse) {
    Set jsonObject               = [].%FromJSON(tHttpRequest.HttpResponse.Data)
    Do jsonObject.%ToJSON(.stream)
    set pContainer.Stream = stream
    $$$TRACE("stream size:"_stream.Size)
    $$$TRACE("stream data:"_stream.Read(1000))

    set sc = ..SendRequestSync("EP.operation.replication",pContainer,.pResponse)
    
   }
   return sc
}

Storage Default
{
<Data name="replicationDefaultData">
<Subscript>"replication"</Subscript>
<Value name="1">
<Value>Server</Value>
</Value>
<Value name="2">
<Value>Port</Value>
</Value>
<Value name="3">
<Value>classname</Value>
</Value>
</Data>
<DefaultData>replicationDefaultData</DefaultData>
<Type>%Storage.Persistent</Type>
}

}

Le code du Business Service :
 

Class EP.service.replication Extends Ens.BusinessService
{

Property Adapter As Ens.InboundAdapter;

Parameter ADAPTER = "Ens.InboundAdapter";

Property TargetConfigNames As %String(MAXLEN = 1000);

Property sync As %Boolean [ InitialExpression = 1 ];

Parameter SETTINGS = "sync:Advanced,TargetConfigNames:Basic:selector?multiSelect=1&context={Ens.ContextSearch/ProductionItems?targets=1&productionName=@productionId}";

Method OnProcessInput(pInput As %RegisteredObject, Output pResponse As Ens.StringResponse) As %Status
{
  set tSC = $$$OK
  for iTarget=1:1:$L(..TargetConfigNames, ",") {
        set tOneTarget=$ZStrip($P(..TargetConfigNames,",",iTarget),"<>W")  Continue:""=tOneTarget
        $$$TRACE("The target '"_tOneTarget_"' will be called "_$SELECT(..sync=1:"synchronously",..sync=0:"synchronously"))
        if ..sync {
          set tSC1=..SendRequestSync(tOneTarget,pInput,.pResponse)  Set:$$$ISERR(tSC1) tSC=$$$ADDSC(tSC,tSC1)
        } else {
          set tSC1=..SendRequestAsync(tOneTarget,pInput)  Set:$$$ISERR(tSC1) tSC=$$$ADDSC(tSC,tSC1)
        }
  }
  return tSC
}

/// Return an array of connections for drawing lines on the config diagram
ClassMethod OnGetConnections(Output pArray As %String, pItem As Ens.Config.Item)
{
	Do ##super(.pArray,pItem)
	If pItem.GetModifiedSetting("TargetConfigNames",.tValue) {
		For i=1:1:$L(tValue,",") { Set tOne=$ZStrip($P(tValue,",",i),"<>W")  Continue:""=tOne  Set pArray(tOne)="" }
	}
}

}