TUTO : Réalisation d’une application magasin de prêt et d’emprunt de matérie
-
TUTO : Réalisation d’une application magasin de prêt et d’emprunt de matérie
Bonjour, je vous propose ma vision du fonctionnement d’un magasin.
Pour ce faire, nous allons utiliser Power Apps, SharePoint et Power Automate.Le but de ce tutoriel sera de mettre en place une application magasin qui vous permettra de faire des entrées/sorties.
D’avoir une vision d’où est le matériel de votre société et d’envoyer des rappels automatiques si nécessaire.De mon côté, je travail avec des QR-Codes sur mes outils, toutefois, cela fonctionne aussi avec des NFC https://learn.microsoft.com/fr-fr/power-platform/power-fx/reference/function-readnfc
En premier lieu, nous avons deux listes SharePoint :
TOOL : contient l’ensemble de vos outils disponible.
Nous commençons par nos colonnes :
TO_TOOL (Une seule ligne de texte) : colonne qui contient l’ID unique de votre matériel.
C’est lui qui fera le lien avec votre code-barre.
TO_DESCRIPTION (Une seule ligne de texte) : colonne qui contient la description de votre outil.
TO_DESCRIPTION_SEO (Une seule ligne de texte) : colonne qui contient votre description et des mots-clés pour vos futurs recherches.
TO_LAST_CHECK_DATE (Date et heure) : colonne qui contient la date du dernier check de votre outil.
TO_REMARK (Plusieurs lignes de texte) : colonne qui contient des remarques possibles pour votre outil.
TO_EVENT_TYPE (Une seule ligne de texte) : colonne qui contient l’état de votre outil.
TO_EVENT_DATE (Date et heure) : colonne qui contient la date du dernier mouvement de votre outil.
TO_GID_BORROWER (Une seule ligne de texte) : colonne qui contient l’ID de la personne qui a ou avait l’outil.
TO_IMG_TOOL (Miniature) : Colonne image pour l’image de votre outil.TOOL_TRACKING : contient l’historique des mouvements de vos outils.
TT_TOOL : (Une seule ligne de texte) : colonne qui contient l’ID unique de votre matériel.
TT_GID_BORROWER (Une seule ligne de texte) : colonne qui contient l’ID de la personne qui a ou avait l’outil.
TT_GID_PROVIDER (Une seule ligne de texte) : colonne qui contient l’ID de la personne qui a donné l’outil.
TT_EVENT_TYPE (Une seule ligne de texte) : colonne qui contient l’état de votre outil.
TT_TRACKING_DATE (Date et heure) : colonne qui contient la date et heure du mouvement.Pour commencer, nous lions nos deux tables à notre application Power Apps.
Nous commençons par créér une collection qu’on nommera “COLL_TOOL” qui contiendra nos différents outils et leur information.
Le but étant d’avoir un gain de ressources pour éviter de faire constamment des requêtes vers notre base de données.
ClearCollect(COLL_TOOL;TOOL)
Première étape : Premier contact
Une personne souhaite emprunter un outil, nous commençons par le sélectionner.
Ce choix est possible de différente manière, je vous laisse le choix final (input text, combobox, scan, …)
De mon côté, j’ai donné deux options, via un combobox et via un scanner qui viendra au final sélectionner la personne dans mon combobox.Seconde étape : l’enregistrement dans une collection des outils a emprunté.
Dès que l’endroit du choix de la personne a qui vous allez prêter vos outils n’est plus vide, vous faites apparaître l’étape suivante.
Ici en l’occurence, je fais apparaitre mon second bouton dès que mon combobox contient une valeur.
Je vous laisse le choix des conditions d’apparition de votre second bouton selon vos nécessités dans la propriété “OnVisible“.
Toujours dans ce même bouton, je vais à la propriété “OnSelect“.
Dans celle-ci, je vais utiliser la fonction SetFocus.
Cette fonction va nous permettre de mettre le focus sur un élément de notre écran pour qu’on puisse interagir avec.
SetFocus(txtMaskCodeTool)
Vous allez rajouté un Saisie de texte(InputText) que vous nommez “txtMaskCodeTool”.
Celui-ci est la clé de voûte de notre système.Troisième étape : vérification et enregistrement en continu.
Dans notre nouveau InputText “txtMaskCodeTool” nous allons à sa propriété “OnChange“.
La première vérification que nous pouvons faire, est que l’outil que nous scannons a bien le schéma qu’on attend.
De mon côté, tous mes codes ont le format “TT” suivi de 5chiffres.
Si ce n’est pas valide, je vais remettre à zéro mon InputText “txtMaskCodeTool”.
If(
/*
Left(Self.Text;2)="TT" Je vérifie d'abord qu'on commence bien par "TT" en prenant les deux premiers caractères.
IsNumeric(Right(Self.Text;5)) Je prends les 5caractères en partant de la droite de mon texte pour vérifier si se sont bien des chiffres.
Len(Self.Text)=7 Je vérifie que l'ensemble fait bien 7caractères.
*/
Left(Self.Text;2)="TT" && IsNumeric(Right(Self.Text;5)) && Len(Self.Text)=7;
;
Reset(Self)
)
Nous aurions pu utiliser une regex aussi pour cette condition.
Si ma condition est correcte et donc que le code que je viens de scanner correspond bien, je vais vérifier si le code que je viens de scanner existe bien dans mes éléments de ma collection “COOL_TOOL”.
Dès lors, nous arrivons à ceci :
If(
Left(Self.Text;2)="TT" && IsNumeric(Right(Self.Text;5)) && Len(Self.Text)=7;
If(
//Ici ma condition de vérification pour voir si le texte qui est actuellement dans mon InputText "txtMaskCodeTool" est ou pas dans ma collection "COLL_TOOL". Si ce n'est pas le cas, je remets à zéro mon InputText "txtMaskCodeTool".
!IsBlank(LookUp(COLL_TOOL;TO_TOOL=Self.Text));
Reset(Self)
);
Reset(Self)
)
Ma dernière vérification sera de ne pas créé de doublon.
Dans mon projet, un outil est unique.
Je prête un objet donc il a SON code barre UNIQUE.
If(
Left(Self.Text;2)="TT" && IsNumeric(Right(Self.Text;5)) && Len(Self.Text)=7;
If(
!IsBlank(LookUp(COLL_TOOL;TO_TOOL=Self.Text));
If(
//Je vérifie que je n'ai pas déjà scanné cet élément en utilisant la fonction LookUp dans ma collection qui contient mes éléments scannés "COLL_TOOL_MOVE_BARCODE" dans la colonne "BARCODE"
//. Si c'est déjà le cas, je remets à zéro mon InputText "txtMaskCodeTool".
IsBlank(LookUp(COLL_TOOL_MOVE_BARCODE;BARCODE=Self.Text));
Reset(Self)
);
Reset(Self)
);
Reset(Self)
)
Dernière étape : ajout de l’élément
Je rajoute dans ma collection “COLL_TOOL_MOVE_BARCODE” le nouvel élément qu’on vient de scanner.
Pour ce faire, nous renseignons deux informations.
1) pour la colonne BARCODE : le code qui vient d’être scanner
2) pour la colonne STATUS : vide
Et après l’ajout, je remets à zéro mon InputText “txtMaskCodeTool”.Collect(COLL_TOOL_MOVE_BARCODE;{BARCODE: Self.Text;STATUS:""});;
Reset(Self);
Le code entier qu’on vient de réaliser :
If(
Left(Self.Text;2)="TT" && IsNumeric(Right(Self.Text;5)) && Len(Self.Text)=7;
If(
!IsBlank(LookUp(COLL_TOOL;TO_TOOL=Self.Text));
If(
IsBlank(LookUp(COLL_TOOL_MOVE_BARCODE;barcode=Self.Text));
Collect(COLL_TOOL_MOVE_BARCODE;{BARCODE: Self.Text;STATUS:""});;
Reset(Self);
Reset(Self)
);
Reset(Self)
);
Reset(Self)
)
⚠️ ATTENTION ⚠️
De notre côté, nous ne souhaitions pas qu’un utilisateur puisse encoder manuellement une donnée.
De ce fait, j’ai masqué l’InputText “txtMaskCodeTool”.
Toutefois, quand je parle de masquer, je veux dire cacher et non utiliser la propriété “OnVisible” sur false.
Si je mettais ma propriété “OnVisible” sur false pour mon InputText “txtMaskCodeTool” celui-ci ne fonctionnerait pas avec la fonction SetFocus.
Vous devez donc le masquer en utilisant une approche différente.
De mon côté, je le place en dehors de l’écran en mettant dans sa propriété Position “X”
Parent.Width
LA GALERIE :
Cette galerie a pour but de réaliser un dernier check-up avant l’enregistrement.
Via celle-ci nous affichons l’ensemble de ce que contient notre collection “COLL_TOOL_MOVE_BARCODE”.
Comme vous pouvez le voir, dans ma galerie, j’ai un sélecteur qui permet d’indiquer si c’est une entrée ou une sortie.
Au début, je voulais vérifier automatiquement, via ma base de données l’état et enregistrer l’inverse.
Mais, imaginons ceci :
– Votre personnel fait sortir un outil, l’encodage automatique le met en “OUT”.
– L’emprunteur ramène votre outil.
– Votre personnel oubli d’encoder l’outil car il a une distraction mais le replace tout de même dans son stock.
– Un autre emprunteur souhaite l’outil.
– Votre personnel le fait sortir, sauf que pour votre système l’outil était encore en “OUT” donc il le place automatiquement en “IN”.Le souci qui va se présenter à nous, est que la prochaine fois que l’outil va vouloir être sorti, il va être placé en “IN” et non en “OUT” et à partir de là, vous n’avez que des erreurs d’encodage pour cet outil. (Jusqu’à ce que vous vous en aperceviez.)
Dans ce sélecteur à la propriété “OnChange“, j’ai ajouté ce bout de code :
Patch(COLL_TOOL_MOVE_BARCODE;ThisItem;{STATUS:DropdownCanvas4.Selected.Value})
On enregistre notre sélection pour l’item en question.Vous pouvez rajouter un bouton “Corbeille” pour supprimer l’élément si vous en avez besoin.
Dans sa propriété “OnSelect” vous mettez dès lors ceci :Remove(COLL_TOOL_MOVE_BARCODE;ThisItem)
J’ai rajouté un InputText pour pouvoir écrire des remarques, l’état, etc si besoin.
De notre côté, on souhaitait que dès que notre combobox de STATUS soit sur “IN” la personne en charge soit obligé d’ajouter un commentaire.
J’ai donc ajouté à la propriété “BorderColor” de cet InputText, ce code :If(DropdownCanvas4.Selected.Value = "IN";Color.Red;RGBA(0; 0; 0; 1))
Ce qui fait que dès qu’il sélectionne le status “IN” la bordure est en rouge pour le prévenir qu’il a l’obligation de remplir ce champ.
Mon dernier bouton est mon bouton d’enregistrement de ma collection.
Tout d’abord, nous devons mettre des conditions sur sa propriété “OnVisible”.
1) Je commence par vérifier que ma collection n’est pas vide :
!IsEmpty(COLL_TOOL_MOVE_BARCODE)
Et oui, pas besoin d’afficher le bouton si nous n’avons rien dans notre collection.2) Je vérifie qu’entre temps, nous avons bien toujours une personne de sélectionné dans notre combobox.
(Len(ComboBox1.Selected.EM_GID_EMPLOYEE)>0 || !IsEmpty(ComboBox1.SelectedItems))
Vous pouvez aussi par choix, enregistrer la personne directement dans une autre colonne de notre collection.3) Je vérifie que dans notre collection, nous avons bien pour tous nos éléments un STATUS.
CountRows(Filter(COLL_TOOL_MOVE_BARCODE;Len(STATUS)=0))=0
4) Je vérifie que l’ensemble des InputText pour les remarques soient remplis de minimum 2caractères si mon sélecteur de status est sur “IN”.
CountRows(
Filter(
Gallery6.AllItems;
DropdownCanvas4.Selected.Value = "IN" &&
Len(TextInputCanvas3.Value) < 2
)
) = 0
Mon code final pour la propriété “OnVisible” :
!IsEmpty(COLL_TOOL_MOVE_BARCODE) && (Len(ComboBox1.Selected.EM_GID_EMPLOYEE)>0 || !IsEmpty(ComboBox1.SelectedItems)) && CountRows(Filter(COLL_TOOL_MOVE_BARCODE;Len(STATUS)=0))=0 && If(
CountRows(
Filter(
Gallery6.AllItems;
DropdownCanvas4.Selected.Value = "IN" &&
Len(TextInputCanvas3.Value) < 2
)
) = 0;
true;
false
)
Propriété “OnSelect”
A partir de cette propriété, nous allons effectué nos enregistrement.
Nous avons notre collection “COLL_TOOL_MOVE_BARCODE” qui contient nos éléments.
Nous devons la parcourir et pour ce faire, nous utilisons la fonction “ForAll”.Nous allons enregistrer dans nos deux bases de données “TOOL_TRACKING” et “TOOL”.
Pour ma première base de données “TOOL_TRACKING”, vous voyez que je créé une nouvelle ligne en utilisant “Defaults(TOOL_TRACKING).
Je mets ensuite l’ensemble de mes valeurs.
Ceci va différer selon ce que vous avez déjà mis en place.
Vous pouvez voir que j’utilise une variable “WHO_USER” qui contient un ID unique m’indiquant dans toute notre société qui est cette personne.Si vous vous demandez pourquoi, j’enregistre aussi l’information dans ma table “TOOL”, voici l’explication.
On veut pouvoir avoir un inventaire de nos éléments dans une autre vue avec l’affichage de l’état de chaque matériel.
Si je comptais que sur ma table “TOOL_TRACKING” je pourrais sortir les 2000premiers éléments.
Au début, je n’aurai pas de souci, mais une fois que j’aurai dépassé mes 2000éléments, et qu’un outil ne serait pas sorti depuis très longtemps.
Il serait potentiellement possible qu’il n’apparaisse pas puisque pas dans mes 2000éléments.ForAll(
COLL_TOOL_MOVE_BARCODE As GetInfosBarcode;
Patch(
TOOL_TRACKING;
Defaults(TOOL_TRACKING);
{
Titre: "TT";
TT_TOOL: GetInfosBarcode.barcode;
TT_GID_BORROWER: VAR_TOOL_SCANNING_GID;
TT_GID_PROVIDER: WHO_USER;
TT_EVENT_TYPE: GetInfosBarcode.STATUS;
TT_TRACKING_DATE: Now();
TT_REMARK: LookUp(Gallery6.AllItems; Title8.Text=GetInfosBarcode.barcode; TextInputCanvas3.Value)
}
)
;;
Patch(TOOL;
LookUp(TOOL;TO_TOOL=GetInfosBarcode.barcode);
{
TO_EVENT_TYPE: GetInfosBarcode.STATUS;
TO_GID_BORROWER: VAR_TOOL_SCANNING_GID;
TO_EVENT_DATE: DateValue(Today())
}
)
)
Après ceci, vous pouvez reset vos éléments (inputtext,combobox, collection,..) et refresh votre liste “TOOL”.
Nous en avons fini avec la partie “Power Apps”.
On peut toujours améliorer, rajouter des éléments etc etc..
Si vous regardez ma capture d’écran, dans ma galerie, j’ai un inputttext pour écrire une remarque.
C’est à vous de mettre en place des éléments aussi selon vos besoins 😉POWER AUTOMATE
Ici, notre but va être de faire une vérification journalière et d’envoyer un email aux personnes ayant dépassé un délai pour leur emprunt.
On commence par choisir une action “Reccurence” car nous voulons agir tous les jours.
J’initialise deux variables avec en type “Chaîne” LISTEOBJ et LISTEOBJFINAL.
De mon côté, j’ai une base de données qui contient l’ensemble de mes employées
Je commence par récuperer l’ensemble de celle-ci avec un carte “Sharepoint” Obtenir les éléments”.Je vais boucler sur mes éléments obtenu.
Dans ma boucle, je vais réutiliser un “Sharepoint” Obtenir les éléments” mais cette fois ci pour ma base de données “TOOL”.
Avec dans la case requête de filtre ceci :
TO_EVENT_TYPE eq 'OUT' and TO_GID_BORROWER eq '@{items('foreach-1')?['EM_GID_EMPLOYEE']}' and TO_EVENT_DATE eq '@{formatDateTime(addDays(utcNow(), -3), 'yyyy-MM-dd')}'
1) je filtre sur mes éléments qui sont en état “OUT” dans ma colonne “TO_EVENT_TYPE”
2) je filtre sur mes éléments qui ont l’ID de la personne actuelle dans la colonne “TO_GID_BORROWER”
3) ⚠️ Le plus important ⚠️ je filtre sur ma colonne “TO_EVENT_DATE” avec comme condition que je ne veux que les éléments qui sont là depuis 3jours.
Donc si vous voulez laissé plus de temps ou moins, c’est à cet endroit que vous devez paramétrer ce choix..Je choisi une carte “condition”.
Dans celle-ci, je regarde si ce que j’obtiens de ma requête est supérieur à 0
length(body('getRows(TT-TECH)')?['value'])
Si on entre dans le “OUI”, je commence avec une boucle de mes éléments obtenu dans ma requête sur ma base de données “TOOL”.
J’ajoute à ma variable de chaîne “LISTEOBJ” ceci :
@{items('foreach-1-1')?['TO_TOOL']} @{items('foreach-1-1')?['TO_DESCRIPTION']}
Ceci en finalité, me donnera une chaine de l’ensemble des éléments pour la personne sur laquelle ma boucle de départ est lancée.
Ainsi, dans un seul email, je vais pouvoir afficher l’ensemble des éléments à la disposition de l’emprunteur qui sont en retards.Je sors de ma boucle et en dessous je défini dans ma variable “LISTEOBJFINAL” ceci :
<div style="text-align: left;">@{variables('LISTEOBJ')}
En dessous de cette carte, je place une carte, “Envoyer un e-mail (V2)”.
Pour le *A
Vous devez prendre l’information dans votre première boucle pour savoir sur quelle vous êtes entrain de boucler.
Pour l’objet, vous avez libre choix :-p
Dans le corp de l’email, vous aurez juste à écrire votre texte et ajouter votre variable “LISTEOBJFINAL”.Pour finir, vous devez rajouter deux cartes “Définir une variable” pour les variables “LISTEOBJ” et LISTEOBJFINAL” et vous mettez la valeur à null via une expression.
C’est très important.
Ceci va permettre de remettre à zéro vos deux variables avant le passage à la personne suivante.
Si vous oubliez cette étape, vous allez vous retrouver à envoyer des emails avec des valeurs éronnées.Nous avons enfin fini !
Bien évidemment, vous pouvez très bien reproduire l’ensemble du processus pour d’autres délais en utilisant la carte “NON” dans notre condition après l’obtention des éléments dans “TOOL”.Voilà voilà, j’espère avoir été le plus clair possible.
Ce ne fut pas un exercice très évident mais j’ai fais de mon mieux.
Je reste disponible si vous avez des questions et aussi pour échanger pour améliorer mes méthodes 😀
Connectez-vous pour répondre.