OData pagination avec l'API
La notion de pagination est fondamentale, il est important de savoir que l’API Sage 100 renvoie à chaque demande un maximum de 100 enregistrements.
Pourquoi limiter à 100 enregistrements ?
Les données requêtées par l’API Sage 100 vont transiter entre le serveur local Sage 100 et Microsoft 365. Si une table de votre base de données contient des milliers d’enregistrements, vous ne pouvez pas les envoyer en une seule demande à la fois pour des raisons de volumétrie et de sécurité.
Pour éviter ce problème, le serveur peut limiter le nombre d’enregistrements qu’il envoie à chaque demande.
Pour l’API Sage 100 cette limite a été fixée à 100.
Note : cette limite s’applique pour les requêtes sur les ressources, les appels de fonction pour récupérer des données telles que les statistiques clients ou statistiques articles ne sont pas concernées par cette limite.
Notion de pagination
Exemple
Imaginons que votre app Power Apps souhaite afficher une liste de clients selon des critères de filtres :
- Votre dossier contient 6500 clients,
- le filtre actuellement défini par l’utilisateur a été transmis à travers votre requête au serveur, le serveur a trouvé localement 175 clients répondant aux critères de filtre,
- côté app Power Apps, la requête va retourner 100 enregistrements et non 175,
- pour que l’utilisateur puisse consulter les 175 résultats, il est nécessaire que vous définissiez une pagination,
- vous pourrez pour cela prévoir un bouton Suivant pour que votre utilisateur puisse consulter les 100 premiers résultats puis les 75 suivants.
L’application d’exemple Vision Clients exploite la pagination lors de la recherche des clients,
voir : Exemples / Vision Clients.
Utilisation de $count et $skip
Pour gérer la pagination vous devez connaître le nombre total d’enregistrements répondant aux critères de filtre de votre requête et le nombre d’enregistrements renvoyés à chaque appel afin de définir le nombre d’enregistrements à sauter en utilisant $skip
.
Paramètre | Type | Description |
---|---|---|
$skip | integer | Jeton de pagination pour obtenir l’ensemble des résultats suivants |
$count | boolean | Nombre total d’éléments répondants à la requête |
- Le paramètre
$count
doit être affecté àtrue
pour pouvoir récupérer le nombre d’enregistrements répondant aux critères de filtre.
Il faudra alors récupérer la valeur via'@odata.count'
. - Le paramètre
$skip
permet de mentionner le nombre d’enregistrements à sauter qui ne seront par conséquent pas inclus dans le résultat.
Exemple
Depuis Power Apps, récupérer le nombre de comptes de charge du plan comptable (comptes de classe 6) :
$count
est forcé àtrue
pour que le total d’enregistrements répondant au filtre$filter
soit disponible dans'@odata.count'
- Dans cet appel
$top
est forcé à 0 puisqu’on ne souhaite pas récupérer les enregistrements mais juste le total, inutile donc que le serveur renvoie des données autre que ce total. '@odata.count'
renvoie le total réel calculé côté serveur, ce total peut donc être supérieur à la limite des 100 enregistrements que l’API renverra à chaque appel.
Set(currentCompanyId;"{datasetId}");;
Sage100cloud.ComptesGetByDatasetid(
currentCompanyId;
{
'$filter': "startswith(numero,'6') and type eq 'Detail'";
'$top': 0;
'$count': true
}
).'@odata.count';;
Maintenant que nous connaissons le total d’enregistrements ('@odata.count'
) et le nombre d’enregistrements (par défaut 100), nous pouvons gérer la pagination.
Il suffira d’incrémenter à chaque appel $skip
avec le nombre total d’enregistrements déjà lus.
Exemple
- Le total est de 257 enregistrements,
- le maximum par appel est 100 enregistrements,
- le premier appel se fait avec
$skip:0
, les enregistrements de 1 à 100 sont retournés, - le second appel se fait avec
$skip:100
, les enregistrements de 101 à 200 sont retournés, - le troisième se fait avec
$skip:200
, les enregistrements de 201 à 257 sont retournés, - pas d’appels supplémentaires nécessaires,
$skip+100
étant maintenant supérieur au total 257.
En comparant le total récupéré via'@odata.count'
et la valeur courante affectée à$skip
il est facilement possible de désactiver ou activer des boutons Précédent et Suivant et d’associer à chacun de ces boutons un traitement pour augmenter le compteur passé à$skip
ou le diminuer puis appeler de nouveau la requête.
Notes importantes
- Si
$orderby
est défini pour trier le résultat, il s’applique toujours côté serveur et donc s’applique à la totalité du résultat et non aux 100 premiers enregistrements. - Par contre si vous appliquez dans Power Apps un tri Sort ou SortBYColumns ce tri sera local à la collection.
Si par exemple vous récupérez les 100 factures les plus récentes en estimant que ce nombre est suffisant pour l’utilisateur, si ensuite côté PowerApps vous appliquez unSort
sur la collection, vous ne triez que les 100 enregistrements récupérés à l’inverse du$orderby
qui s’applique à la totalité du résultat côté serveur. - Remarque identique si vous appliquez un Filter ou Search ou LookUp , le filtre ne s’appliquera qu’au 100 enregistrements à l’inverse de
$filter
qui s’applique à la totalité des données côté serveur.
Recommandations
Dossier de tests avec suffisamment de données
Nous vous recommandons d’utiliser un dossier avec suffisamment de données pour ne pas être piégés si pour certaines requêtes le seuil des 100 enregistrements n’est pas atteint.
Par exemple, si vous testez avec la société BIJOU de démonstration Sage 100, comme ce dossier a un nombre de clients, de devis, de factures inférieur à 100, vous pourriez croire, si vous avez omis de gérer la pagination, que votre développement est correct.
Mais une fois mise en production, votre application utilisée avec un dossier contenant des données réelles avec plus de 100 clients, ou devis ou factures, empêcherait l’utilisateur d’accéder aux données au delà des 100 premiers enregistrements.
Ou simuler la limite à moins de 100 enregistrements
Une astuce consiste aussi à définir un paramètre global nommé par exemple maxPaginate
avec une valeur inférieure au nombre d’enregistrements moyens de votre dossier de tests.
Par exemple vous pouvez affecter maxPaginate
avec la valeur 10.
Puis lors de vos requêtes, affectez toujours le paramètre $top
avec maxPaginate
, ainsi vos requêtes ne renverront que le nombre maximal mentionné dans maxPaginate
.
Vous serez alors plus rapidement alertés lors de la conception de votre application que vous devez gérer la pagination pour permettre à l’utilisateur d’accéder à l’ensemble des données et non à une vue tronquée des données.
Une fois la pagination testée et votre développement opérationnel, vous pourrez affecter maxPaginate
avec la valeur 100 et votre développement continuera à fonctionner.
Paramètre | Type | Description |
---|---|---|
$top | integer | Nombre d’éléments à retourner dans la réponse |
Exemple
L’exemple ci-dessous permet de simuler que l’API va renvoyer un maximum de 10 enregistrements au lieu de la limite standard de 100 enregistrements.
Bien évidemment, pour tester la pagination dans ce contexte, il faudra que $skip
soit aussi incrémenté par pas de 10 et non par pas de 100.
L’exemple illustre l’appel à la première page avec $skip
affecté à 0. Pour les pages suivantes, il faudra incrémenter cptPage
de 1 à chaque fois pour que $skip
contienne 10 puis 20, etc. et non 100 puis 200, etc.
Set (maxPaginate;10);; // Valeur max du $top pour les tests
Set (cptPage;0);; // Compteur de pages pour affecter $skip
...
Set(currentCompanyId;"{datasetId}");;
Sage100cloud.ComptesGetByDatasetid(
currentCompanyId;
{
'$expand': Blank();
'$select': "intitule,numero";
'$filter': Blank();
'$orderby':"numero";
'$top': maxPaginate;
'$skip': cptPage*maxPaginate;
'$count': Blank()
}
).value;;
Note : Bien que la valeur maximale du nombre d’enregistrements retournés par l’API soit 100, rien ne vous oblige à afficher une pagination de 100 enregistrements à la fois.
Vous pouvez par exemple décider que chaque page contiendra 50 enregistrements, c’est le cas des exemples Vision Clients et Catalogues et Stocks qui gèrent une liste de 50 clients ou 50 articles par page.
Boucler sur la pagination pour récupérer tous les enregistrements
Pour certains usages, il peut être nécessaire de récupérer la totalité des enregistrements.
Ce peut être le cas lorsque l’on sait que le nombre total d’enregistrements restera faible mais néanmoins risque de dépasser 100.
A l’exemple des familles de produits que l’on souhaiterait choisir dans une liste déroulante, s’il y a 130 familles il faut que ces 130 familles apparaissent dans la liste de choix.
En appliquant la logique de pagination, il est possible de boucler pour récupérer la totalité des enregistrements.
L’exemple Power Apps ci-dessous montre :
- comment récupérer la totalité des familles de produit dans une collection
familles
, - la collection contiendra aussi en premier une valeur
<Tous>
pour permettre ce choix lors de l’utilisation dans une liste déroulante. - comme il n’existe pas de
for i
dans Power Apps mais unForAll
qui boucle sur les enregistrements d’une collection, on simule une collection où chaque enregistrement est un | avec un nombre de | égal au nombre de pages à récupérer, - on fait un premier appel à la requête des familles avec
$count
àtrue
afin de récupérer le nombre d’enregistrements via@odata.count
, dans cet appel on a forcé$top
à 0 puisqu’on ne souhaite pas récupérer les enregistrements mais juste le total, inutile donc que le serveur renvoie des données autre que ce total, - bien sûr pour récupérer ce nombre d’enregistrements, on applique via
$filter
le même filtre que celui qui sera mentionné pour récupérer les lignes, ici le filtre sur les familles dont la valeur de la propriététype
est égale à Detail. - dans la boucle
ForAll
, on incrémente$skip
avec le nombre de familles déjà récupérées dans la collection : 0 puis 100 puis 200, etc, - à noter qu’on ne tient pas compte de l’enregistrement
<Tous>
dans le calcul de la pagination, d’où la variableRowsInit
qui contient le nombre initial d’enregistrements de la collectionfamilles
, ici 1 pour l’enregistrement<Tous>
, et le calcul de$skip
qui déduit cet enregistrement :CountRows(familles) - RowsInit
, - dans l’exemple on utilise un
AddColumns
pour ajouter une nouvelle colonne dans la collectionFamilles
qui n’existe pas en tant que propriété dans la ressourceFamilles
de l’API. Ici la colonneintituleComplet
est créée à partir de la valeur de la propriétéintitulé
suivie de la valeur de la propriétécode
entre parenthèses.
//utilisé itérer au prorata du nombre de pages de familles
Set(colIterate;"||||||||||||||||||||||||||||||||||||");;
//Nombre maxi d'enregistrements renvoyés par l'API
Set(apiPagesize;100);;
//Initialisation collection familles
ClearCollect(familles;{code:"";intitule:""});;Clear(familles);;
// Ajout en premier du choix <Tous>
ClearCollect(familles;{code:"<Tous>";intitule:"<Tous>"});;
With (
{
// Nb de pages = @odata.count / apiPagesize
MaxIterate:RoundUp(
Sage100cloud.FamillesGetByDatasetid(currentCompanyId;{'$top':0;'$count':true;'$filter':"type eq 'Detail'"}).'@odata.count'/apiPagesize
;0);
// Nb enregistrements collection famille avant ajout des données famille
RowsInit:CountRows(familles)
};
//On utilise les MaxIterate car. de la chaîne colIterate pour provoquer une boucle car pas de for i dans Power Apps.
ForAll(
Split(Left(colIterate;MaxIterate);"|");
Collect(familles;AddColumns(Sage100cloud.FamillesGetByDatasetid(currentCompanyId;
{
'$select':"intitule,code";
'$top':apiPagesize;
'$skip':CountRows(familles)-RowsInit;
'$filter':"type eq 'Detail'"
}).value;
"intituleComplet";intitule&" ("&code&")")
)
)
);;
Vous trouverez un exemple similaire dans l’application d’exemple Exemples / Catalogues et stocks
dans le code OnSelect
de l’objet Choix sociétés.