Créer une application de demandes de vacances avec Power Apps, Power Automate et SharePoint (approbation du gestionnaire et soldes de congés)
Les demandes de vacances par courriel fonctionnent bien... jusqu'au jour où ça ne fonctionne plus. La demande de quelqu'un reste non lue dans la boîte de réception d'un gestionnaire, les RH apprennent l'existence d'un congé après qu'il a été pris, et personne ne sait trop combien de jours il reste à chacun.
Dans ce guide, nous allons créer un système simple de demandes de congé dans Microsoft 365 : une application canevas Power Apps pour les employés, un flux d'approbation Power Automate qui est acheminé au gestionnaire de chaque employé, et des listes SharePoint comme système dorsal. Les gestionnaires peuvent approuver, refuser ou demander d'autres dates en précisant la raison, et l'employé reçoit un courriel dans tous les cas. Les jours sont déduits du bon solde (vacances, maladie ou congé personnel) dès qu'une demande est soumise, et sont recrédités si elle n'est pas approuvée.
Le tout est aussi bien sécurisé. Les employés ne voient que leurs propres demandes, les gestionnaires voient celles de leur équipe, et les administrateurs voient tout. Cette règle est appliquée par les autorisations SharePoint au niveau des éléments, et pas seulement par des filtres dans l'application.

Ce que vous allez créer
Une application canevas où les employés choisissent un type de congé et des dates, voient combien de jours ouvrables la demande utilise et la soumettent.
Des tuiles de solde par type de congé (Vacation, Sick, Personal). L'application bloque les demandes qui dépassent le solde.
Un flux d'approbation qui achemine chaque demande au gestionnaire de l'employé défini dans Microsoft Entra ID (Azure AD), avec un approbateur de secours si aucun gestionnaire n'est défini.
Trois issues : Approved (approuvée), Rejected (refusée) ou Change dates (dates à modifier), chacune avec les commentaires du gestionnaire.
Un courriel de résultat à l'employé avec un badge d'état en couleur et son solde restant.
Déduction et remboursement automatiques : les jours sont déduits à la soumission de la demande et recrédités si elle est refusée ou renvoyée pour de nouvelles dates.
Une sécurité au niveau des éléments, pour que chaque demande ne soit visible que par l'employé, son gestionnaire et les administrateurs.
Ce qu'il vous faut
Microsoft 365 avec SharePoint Online, Outlook et Power Apps / Power Automate (les connecteurs standard utilisés ici sont inclus dans la plupart des forfaits pour entreprises; aucune licence premium n'est nécessaire).
L'autorisation de créer un site SharePoint, ou un site existant dont vous êtes propriétaire.
Des gestionnaires renseignés dans les profils utilisateur de Microsoft Entra ID, pour que le flux puisse trouver l'approbateur de chaque employé.
Étape 1 : Créer le site et les listes SharePoint
Créez un nouveau site d'équipe ou de communication, par exemple HR Requests. Ajoutez-y ensuite trois listes.
Liste Vacation Requests
C'est ici que se trouvent toutes les demandes. Renommez la colonne Title en Request, puis ajoutez les colonnes suivantes :
Employee : Personne
EmployeeEmail : Une seule ligne de texte
Manager : Personne
ManagerEmail : Une seule ligne de texte
LeaveType : Choix (Vacation, Sick, Personal), Vacation par défaut
StartDate et EndDate : Date seulement
Days : Nombre
EmployeeNotes : Plusieurs lignes de texte
Status : Choix (Pending, Approved, Rejected, Change dates), Pending par défaut
ManagerComments : Plusieurs lignes de texte
Nous stockons les courriels en texte brut en plus des colonnes Personne, car les colonnes de texte se filtrent facilement dans Power Apps, sans avertissement de délégation.

Liste Leave Balances
Une ligne par employé et par type de congé :
Title (renommée Employee Email) : le courriel de l'employé, en minuscules
Employee : Personne
LeaveType : Choix (Vacation, Sick, Personal)
Allowance : Nombre (le droit annuel)
Booked : Nombre, 0 par défaut
Balance : Colonne calculée, avec la formule ci-dessous et le type de résultat réglé sur Une seule ligne de texte
=(Allowance-Booked)&""Pourquoi du texte? Une colonne calculée de type nombre est renvoyée à Power Apps et à Power Automate sous la forme « 6.00000000000000 ». La renvoyer en texte donne un résultat propre.

Liste App Admins
Une liste simple qui ne contient que la colonne Title. Ajoutez une ligne par administrateur avec son adresse courriel en minuscules. L'application s'en sert pour activer la vue All requests, et le flux utilise le premier administrateur comme approbateur de secours.
Étape 2 : Définir les autorisations des listes
Des filtres dans une application ne sont pas de la sécurité. Quiconque a accès à la liste pourrait l'ouvrir dans SharePoint et tout voir; nous verrouillons donc d'abord les listes, puis laissons le flux accorder l'accès demande par demande.
Site : accordez à votre personnel (par exemple « Tout le monde sauf les utilisateurs externes » dans le groupe Membres du site) l'autorisation Lecture sur le site.
Vacation Requests : arrêtez l'héritage des autorisations et accordez Collaboration aux Membres, pour que les employés puissent créer des demandes depuis l'application. Les Propriétaires conservent le Contrôle total.
Leave Balances : arrêtez l'héritage des autorisations et accordez Lecture aux Membres. Rompez ensuite l'héritage sur chaque ligne de solde, pour que seuls les propriétaires et l'employé concerné puissent la lire. Seul le flux (exécuté en tant que propriétaire) modifie la colonne Booked.
App Admins : Lecture pour les Membres, afin que l'application puisse vérifier si l'utilisateur actuel est administrateur.
Dans les étapes suivantes, le flux retire le groupe Membres de chaque nouvelle demande et accorde Lecture uniquement à l'employé et à son gestionnaire. Les administrateurs font partie du groupe Propriétaires; ils voient donc toujours tout.
Étape 3 : Créer le flux
Dans Power Automate, choisissez Create > Automated cloud flow. Nommez-le Vacation Request Approval et choisissez le déclencheur SharePoint When an item is created.

Réglez Site Address sur votre site HR Requests et List Name sur Vacation Requests.

Voici le flux complet que nous allons créer, de haut en bas.



Étape 4 : Trouver l'approbateur
D'abord, obtenez un approbateur de secours au cas où l'employé n'aurait pas de gestionnaire dans Entra ID.
Get admins : action SharePoint Get items sur la liste App Admins, avec Top Count réglé sur 1.
Fallback approver : Initialize variable, nom ApproverEmail, type String, avec la valeur suivante :
first(outputs('Get_admins')?['body/value'])?['Title']Get manager : Office 365 Users Get manager (V2), avec User réglé sur la valeur EmployeeEmail du déclencheur.
Use manager as approver : Set variable pour régler ApproverEmail sur la sortie Mail du gestionnaire.
Si l'employé n'a pas de gestionnaire, Get manager échoue et Use manager as approver est ignorée. Sur l'action suivante, ouvrez donc Settings > Run after et cochez à la fois « is successful » et « is skipped ». Le flux continue alors avec l'approbateur de secours.
Save approver on request : action SharePoint Update item sur Vacation Requests, avec l'Id du déclencheur. Réglez Manager Claims sur la variable ApproverEmail et ManagerEmail sur :
toLower(variables('ApproverEmail'))C'est ce qui alimente la vue My team dans l'application.
Étape 5 : Déduire les jours du solde
Get balance : action SharePoint Get items sur Leave Balances, Top Count 1, avec la requête de filtre (Filter Query) suivante :
Title eq '@{toLower(triggerOutputs()?['body/EmployeeEmail'])}' and LeaveType eq '@{triggerOutputs()?['body/LeaveType/Value']}'Deduct days : action SharePoint Update item sur Leave Balances. Utilisez l'ID du premier résultat et réglez Booked sur :
add(float(coalesce(first(body('Get_balance')?['value'])?['Booked'],0)), float(triggerOutputs()?['body/Days']))La déduction au moment de la soumission empêche deux demandes soumises à peu d'intervalle d'utiliser les mêmes jours.
Étape 6 : Verrouiller la demande avec des autorisations au niveau de l'élément
Nous utilisons quatre actions SharePoint Send an HTTP request to SharePoint. Elles pointent toutes vers votre site HR Requests, utilisent la méthode POST et comportent l'en-tête Accept: application/json;odata=nometadata.
Stop inheriting permissions. Uri :
_api/web/lists/getbytitle('Vacation Requests')/items(@{triggerOutputs()?['body/ID']})/breakroleinheritance(copyRoleAssignments=true,clearSubscopes=true)Remove Members group. Cette action retire l'accès Collaboration du groupe Membres sur cet élément précis. Repérez l'ID de votre groupe Membres sous Paramètres du site > Personnes et groupes (il figure dans l'URL sous MembershipGroupId), et remplacez 5 si le vôtre est différent. 1073741827 est l'ID du rôle intégré Collaboration.
_api/web/lists/getbytitle('Vacation Requests')/items(@{triggerOutputs()?['body/ID']})/roleassignments/removeroleassignment(principalid=5,roledefid=1073741827)Get employee ID et Get manager ID. SharePoint a besoin d'un ID d'utilisateur, pas d'un courriel. Le point de terminaison ensureuser le renvoie et ajoute l'utilisateur au site au besoin. Uri :
_api/web/ensureuserCorps pour l'employé (utilisez variables('ApproverEmail') pour le gestionnaire) :
{ "logonName": "@{triggerOutputs()?['body/EmployeeEmail']}" }Employee can read et Manager can read. 1073741826 est le rôle intégré Lecture. Pour le gestionnaire, utilisez plutôt body('Get_manager_ID').
_api/web/lists/getbytitle('Vacation Requests')/items(@{triggerOutputs()?['body/ID']})/roleassignments/addroleassignment(principalid=@{body('Get_employee_ID')?['Id']},roledefid=1073741826)Une fois ces actions exécutées, la demande n'est visible que par l'employé, son gestionnaire et les propriétaires du site (vos administrateurs). L'employé n'a que l'autorisation Lecture; il ne peut donc pas modifier une demande une fois soumise.
Étape 7 : Envoyer la demande d'approbation
Ajoutez l'action Approvals Start and wait for an approval, avec le type d'approbation Custom Responses - Wait for one response. Ajoutez trois options de réponse : Approve, Reject et Change dates.
Titre :
@{triggerOutputs()?['body/LeaveType/Value']} request: @{triggerOutputs()?['body/Employee/DisplayName']}, @{formatDateTime(triggerOutputs()?['body/StartDate'],'MMM d')} to @{formatDateTime(triggerOutputs()?['body/EndDate'],'MMM d, yyyy')}Assigned to : la variable ApproverEmail. Indiquez dans Details le type de congé, les dates, les jours ouvrables, les notes et le solde restant en cas d'approbation. La zone Details prend en charge le Markdown; les étiquettes en gras fonctionnent donc. Pour la ligne du solde, utilisez :
sub(float(first(body('Get_balance')?['value'])?['Balance']), float(triggerOutputs()?['body/Days']))Ajoutez une ligne demandant au gestionnaire de choisir Change dates si le moment ne convient pas et de proposer de nouvelles dates dans les commentaires. Réglez Item link sur le Link to item du déclencheur, pour qu'il puisse ouvrir la demande dans SharePoint.
Le gestionnaire reçoit la demande d'approbation dans Outlook, dans Teams et dans le centre d'approbations de Power Automate.

Étape 8 : Enregistrer la décision et recréditer les jours
Decision status : une action Compose qui convertit la réponse du gestionnaire en nos valeurs d'état :
if(equals(outputs('Start_and_wait_for_an_approval')?['body/outcome'],'Approve'),'Approved',if(equals(outputs('Start_and_wait_for_an_approval')?['body/outcome'],'Reject'),'Rejected','Change dates'))Save decision : Update item sur Vacation Requests. Réglez Status sur la sortie de Decision status et ManagerComments sur :
first(outputs('Start_and_wait_for_an_approval')?['body/responses'])?['comments']Refund if not approved : une condition où la sortie de Decision status n'est pas égale à Approved. Dans la branche True, récupérez de nouveau la ligne de solde (la même action Get items qu'à l'étape 5, nommée Get balance again) et mettez-la à jour avec :
max(0, sub(float(coalesce(first(body('Get_balance_again')?['value'])?['Booked'],0)), float(triggerOutputs()?['body/Days'])))Nous relisons la ligne au lieu de réutiliser celle de l'étape 5, car l'approbation peut prendre des jours et d'autres demandes peuvent avoir modifié le solde entre-temps.
Étape 9 : Envoyer un courriel à l'employé
Read balance : une autre action Get items sur Leave Balances avec le même filtre, pour que le courriel affiche le solde après un éventuel remboursement.
Email the employee : Office 365 Outlook Send an email (V2) à la valeur EmployeeEmail. Objet :
Your @{toLower(triggerOutputs()?['body/LeaveType/Value'])} request: @{outputs('Decision_status')}Dans le corps, passez à la vue code (</>) et construisez un petit tableau HTML : une salutation, un message d'une ligne selon l'état, un badge d'état en couleur, les dates, les jours ouvrables, les commentaires du gestionnaire et les « jours restants ». La couleur du badge tient en une seule expression :
if(equals(outputs('Decision_status'),'Approved'),'#107c10',if(equals(outputs('Decision_status'),'Rejected'),'#a4262c','#8a5a00'))


Enregistrez le flux et activez-le.
Étape 10 : Créer l'application canevas
Dans Power Apps, créez une application canevas vide en format tablette et ajoutez les trois listes comme sources de données : Vacation Requests, Leave Balances et App Admins.
Qui est l'utilisateur?
Sélectionnez App et ajoutez ces formules nommées dans la propriété Formulas :
MyEmail = Lower(User().Email);
IsAdmin = !IsBlank(LookUp('App Admins', Title = MyEmail));
IsManager = !IsBlank(LookUp('Vacation Requests', ManagerEmail = MyEmail));Comme les listes elles-mêmes sont sécurisées, un utilisateur qui n'est pas gestionnaire n'obtient simplement aucune ligne avec le filtre d'équipe. Les boutons sont une commodité, pas la sécurité.
Écran d'accueil
Trois boutons bascules : My requests (toujours), My team (Visible : IsManager || IsAdmin) et All requests (Visible : IsAdmin). Chacun définit une variable varView, par exemple Set(varView, "Team").
Une galerie horizontale pour les tuiles de solde. Items : Filter('Leave Balances', Title = MyEmail). À l'intérieur, affichez ThisItem.LeaveType.Value et Round(Value(ThisItem.Balance), 1) & " days left".
Une galerie verticale pour les demandes, avec une étiquette d'état en couleur et les commentaires du gestionnaire. Sa propriété Items :
SortByColumns(If(varView = "All", 'Vacation Requests', varView = "Team", Filter('Vacation Requests', ManagerEmail = MyEmail), Filter('Vacation Requests', EmployeeEmail = MyEmail)), "Created", SortOrder.Descending)Une couleur d'état dans la propriété Fill de l'étiquette :
Switch(ThisItem.Status.Value, "Approved", RGBA(16,124,16,1), "Rejected", RGBA(164,38,44,1), "Change dates", RGBA(160,100,0,1), RGBA(96,110,125,1))OnVisible de l'écran : Refresh('Vacation Requests'); Refresh('Leave Balances'), pour que les états et les soldes soient toujours à jour.
Voici la vue All requests de l'administrateur, avec le nom de chaque employé sur sa ligne.

Écran de nouvelle demande
Une liste déroulante de types de congé avec Items ["Vacation", "Sick", "Personal"].
Une étiquette masquée qui contient le solde disponible pour le type sélectionné :
Coalesce(Value(LookUp('Leave Balances', Title = MyEmail && LeaveType.Value = ddType.Selected.Value).Balance), 0)Deux sélecteurs de date pour le premier et le dernier jour de congé.
Une étiquette masquée qui compte les jours ouvrables (du lundi au vendredi) entre les deux dates :
With({s: dpStart.SelectedDate, e: dpEnd.SelectedDate}, If(e < s, 0, CountIf(Sequence(DateDiff(s, e, TimeUnit.Days) + 1, 0), Weekday(DateAdd(s, Value, TimeUnit.Days), StartOfWeek.Monday) < 6)))Un message qui affiche les jours utilisés et le solde restant, ou un avertissement en rouge lorsque la demande dépasse le solde.
Un bouton Submit désactivé lorsque les dates sont incorrectes ou que le solde est insuffisant.


La propriété OnSelect du bouton Submit crée l'élément avec Patch. Le flux prend ensuite le relais :
Patch('Vacation Requests', Defaults('Vacation Requests'), { Title: ddType.Selected.Value & " " & Text(dpStart.SelectedDate, "yyyy-mm-dd"), LeaveType: {Value: ddType.Selected.Value}, Employee: { '@odata.type': "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedUser", Claims: "i:0#.f|membership|" & MyEmail, DisplayName: User().FullName, Email: MyEmail, Department: "", JobTitle: "", Picture: "" }, EmployeeEmail: MyEmail, StartDate: dpStart.SelectedDate, EndDate: dpEnd.SelectedDate, Days: Value(lblDaysCalc.Text), EmployeeNotes: txtNotes.Text, Status: {Value: "Pending"} });
Notify("Request sent. Your manager will get an approval request.", NotificationType.Success); Navigate(Screen1, ScreenTransition.UnCover)Enregistrez et publiez l'application, puis partagez-la avec votre personnel (par exemple le même groupe Membres).
Étape 11 : Tester les trois issues
Soumettez quelques demandes depuis l'application et répondez différemment à chaque demande d'approbation :
Change dates, avec un commentaire proposant de nouvelles dates. L'état passe à Change dates, les jours sont recrédités au solde et l'employé reçoit le courriel orangé.
Approve. Les jours restent réservés et le courriel affiche le nouveau solde.
Refusez une demande de congé de maladie. Les jours sont recrédités, et le solde Sick revient à 10.
Connectez-vous ensuite en tant qu'autre employé. Il ne devrait voir que ses propres demandes dans l'application, et SharePoint ne devrait rien lui montrer d'autre dans la liste non plus.
Qui voit quoi
Employé : ses propres demandes (en lecture seule une fois soumises) et ses propres soldes.
Gestionnaire : ses propres demandes, plus toutes celles qui lui sont acheminées, dans la vue My team.
Administrateur : tout, grâce à la vue All requests et au groupe Propriétaires du site.
Le flux : s'exécute avec la connexion du propriétaire du flux; c'est pourquoi le compte propriétaire a besoin du Contrôle total sur le site.
Préparer la mise en production
Jours fériés : tenez une liste Holidays et excluez ces dates dans la formule des jours ouvrables et dans le flux.
Demi-journées : modifiez Days pour accepter les décimales et ajoutez une bascule « demi-journée » dans l'application.
Remise à zéro annuelle : un flux planifié le 1er janvier (ou à votre date anniversaire) qui remet Booked à 0 et applique les règles de report.
Annulations : ajoutez un bouton Cancel pour les demandes Pending ou Approved, qui recrédite les jours.
Intégration des nouveaux employés : un flux qui crée les trois lignes de solde (avec les autorisations par élément) à l'arrivée d'une nouvelle personne.
Compte de service : exécutez le flux avec un compte dédié plutôt qu'avec celui d'une personne, pour qu'il ne cesse pas de fonctionner lorsque quelqu'un quitte l'entreprise.
La partie amusante : le bingo des absences
Cochez tout ce que vous avez déjà vu :
« Je serai en congé vendredi » dans une conversation Teams, envoyé le vendredi matin.
Des vacances approuvées par un « répondre à tous ».
Deux personnes de la même équipe en congé la même semaine, toutes deux « approuvées ».
Une réponse d'absence qui annonce « de retour lundi », datant d'il y a trois lundis.
Quelqu'un qui demande aux RH combien de jours il lui reste. En décembre.
Trois ou plus? Cette application sera rentabilisée avant les Fêtes.
Besoin d'un coup de main?
Smart Solutions conçoit des solutions Power Apps et Power Automate pour les entreprises canadiennes, des approbations de congés et de dépenses jusqu'aux applications complètes de RH et d'exploitation. Communiquez avec nous et nous vous aiderons à déployer cette solution, ou à créer un outil adapté à vos politiques.
Vous gérez aussi vos demandes d'achat, vos fournisseurs et vos approbations par courriel? Découvrez ProcuraCloud, notre plateforme canadienne d'approvisionnement et de gestion d'entreprise pour les PME.




Commentaires