1. Présentation
Dans cet atelier de programmation, vous allez utiliser Google Apps Script pour écrire un module complémentaire Google Workspace pour Gmail. Il permettra aux utilisateurs d'ajouter des données de reçus provenant d'un e-mail à une feuille de calcul directement dans Gmail. Lorsqu'un utilisateur reçoit un reçu par e-mail, il ouvre le module complémentaire, qui récupère automatiquement les informations sur les dépenses pertinentes à partir de l'e-mail. L'utilisateur peut modifier les informations sur les dépenses, puis les envoyer pour les enregistrer dans une feuille de calcul Google Sheets.
Points abordés
- Créer un module complémentaire Google Workspace pour Gmail à l'aide de Google Apps Script
- Analyser un e-mail avec Google Apps Script
- Interagir avec Google Sheets via Google Apps Script
- Stocker des valeurs utilisateur à l'aide du service Properties de Google Apps Script
Prérequis
- Accès à Internet et à un navigateur Web
- Un compte Google
- Certains messages dans Gmail, de préférence des reçus d'e-mails
2. Obtenir l'exemple de code
Tout au long de cet atelier de programmation, il peut être utile de se référer à une version fonctionnelle du code que vous allez écrire. Le dépôt GitHub contient un exemple de code que vous pouvez utiliser comme référence.
Pour obtenir l'exemple de code, exécutez la commande suivante à partir de la ligne de commande :
git clone https://github.com/googleworkspace/gmail-add-on-codelab.git
3. Créer un module complémentaire de base
Commencez par écrire le code d'une version simple du module complémentaire qui affiche un formulaire de dépenses à côté d'un e-mail.
Commencez par créer un projet Apps Script et ouvrez son fichier manifeste.
- Accédez à script.google.com. Vous pouvez y créer, gérer et surveiller vos projets Apps Script.
- Pour créer un projet, en haut à gauche, cliquez sur Nouveau projet. Le nouveau projet s'ouvre avec un fichier par défaut nommé
Code.gs. LaissezCode.gsde côté pour le moment. Vous l'utiliserez plus tard. - Cliquez sur Projet sans titre, nommez votre projet Expense It!, puis cliquez sur Renommer.
- Sur la gauche, cliquez sur Paramètres du projet
. - Cochez la case Afficher le fichier manifeste "appscript.json" dans l'éditeur.
- Cliquez sur Éditeur
. - Pour ouvrir le fichier manifeste, cliquez sur
appscript.jsonà gauche.
Dans appscript.json, spécifiez les métadonnées associées au module complémentaire, telles que son nom et les autorisations requises. Remplacez le contenu de appsscript.json par les paramètres de configuration suivants :
{
"timeZone": "GMT",
"oauthScopes": [
"https://www.googleapis.com/auth/gmail.addons.execute"
],
"gmail": {
"name": "Expense It!",
"logoUrl": "https://www.gstatic.com/images/icons/material/system/1x/receipt_black_24dp.png",
"contextualTriggers": [{
"unconditional": {
},
"onTriggerFunction": "getContextualAddOn"
}],
"primaryColor": "#41f470",
"secondaryColor": "#94f441"
}
}
Portez une attention particulière à la partie du fichier manifeste appelée contextualTriggers. Cette partie du fichier manifeste identifie la fonction définie par l'utilisateur à appeler lorsque le module complémentaire est activé pour la première fois. Dans ce cas, il appelle getContextualAddOn, qui obtient des informations sur l'e-mail ouvert et renvoie un ensemble de cartes à afficher à l'utilisateur.
Pour créer la fonction getContextualAddOn :
- Sur la gauche, pointez sur
Code.gs, puis cliquez sur Menu
> Renommer. - Saisissez
GetContextualAddOn, puis appuyez sur la toucheEnter. Apps Script ajoute automatiquement.gsà votre nom de fichier. Vous n'avez donc pas besoin de saisir d'extension de fichier. Si vous saisissezGetContextualAddOn.gs, Apps Script nomme votre fichierGetContextualAddOn.gs.gs. - Dans
GetContextualAddOn.gs, remplacez le code par défaut par la fonctiongetContextualAddOn:
/**
* Returns the contextual add-on data that should be rendered for
* the current e-mail thread. This function satisfies the requirements of
* an 'onTriggerFunction' and is specified in the add-on's manifest.
*
* @param {Object} event Event containing the message ID and other context.
* @returns {Card[]}
*/
function getContextualAddOn(event) {
var card = CardService.newCardBuilder();
card.setHeader(CardService.newCardHeader().setTitle('Log Your Expense'));
var section = CardService.newCardSection();
section.addWidget(CardService.newTextInput()
.setFieldName('Date')
.setTitle('Date'));
section.addWidget(CardService.newTextInput()
.setFieldName('Amount')
.setTitle('Amount'));
section.addWidget(CardService.newTextInput()
.setFieldName('Description')
.setTitle('Description'));
section.addWidget(CardService.newTextInput()
.setFieldName('Spreadsheet URL')
.setTitle('Spreadsheet URL'));
card.addSection(section);
return [card.build()];
}
L'interface utilisateur de chaque module complémentaire Google Workspace se compose de fiches divisées en une ou plusieurs sections, chacune contenant des widgets qui peuvent afficher et obtenir des informations de l'utilisateur. La fonction getContextualAddOn crée une fiche unique qui obtient des informations sur une dépense trouvée dans un e-mail. La fiche comporte une section contenant des champs de saisie de texte pour les données pertinentes. La fonction renvoie un tableau des fiches du module complémentaire. Dans ce cas, le tableau renvoyé ne contient qu'une seule carte.
Avant de déployer le module complémentaire Expense It!, vous avez besoin d'un projet Google Cloud Platform (GCP), que les projets Apps Script utilisent pour gérer les autorisations, les services avancés et d'autres détails. Pour en savoir plus, consultez la page Projets Google Cloud Platform.
Pour déployer et exécuter votre module complémentaire, procédez comme suit :
- Ouvrez votre projet GCP et copiez son numéro.
- Dans votre projet Apps Script, à gauche, cliquez sur Paramètres du projet
. - Sous "Projet Google Cloud Platform (GCP)", cliquez sur Changer de projet.
- Saisissez le numéro de votre projet GCP, puis cliquez sur Définir le projet.
- Cliquez sur Déployer> Tester les déploiements.
- Assurez-vous que le type de déploiement est Module complémentaire Google Workspace. Si nécessaire, en haut de la boîte de dialogue, cliquez sur Activer les types de déploiement
, puis sélectionnez Module complémentaire Google Workspace comme type de déploiement. - À côté de Application(s) : Gmail, cliquez sur Installer.
- Cliquez sur OK.
Vous pouvez désormais voir le module complémentaire dans votre boîte de réception Gmail.
- Ouvrez Gmail sur votre ordinateur.
- Dans le panneau de droite, l'application Expense It! Le module complémentaire
s'affiche. Vous devrez peut-être cliquer sur Plus de modules complémentaires
pour le trouver. - Ouvrez un e-mail, de préférence un reçu avec des dépenses.
- Pour ouvrir le module complémentaire, cliquez sur Expense It! dans le panneau latéral de droite.
. - Autorisez Expense It! à accéder à votre compte Google en cliquant sur Autoriser l'accès, puis suivez les instructions.
Le module complémentaire affiche un formulaire simple à côté d'un message Gmail ouvert. Il ne fait rien d'autre pour le moment, mais vous développerez sa fonctionnalité dans la section suivante.
Pour voir les modifications apportées à votre module complémentaire au fur et à mesure de cet atelier, il vous suffit d'enregistrer votre code et d'actualiser Gmail. Aucun déploiement supplémentaire n'est nécessaire.
4. Accéder aux e-mails
Ajoutez du code qui récupère le contenu des e-mails et modularisez le code pour une meilleure organisation.
À côté de "Fichiers", cliquez sur Ajouter
> Script, puis créez un fichier nommé Cards. Créez un deuxième fichier de script appelé Helpers. Cards.gs crée la fiche et utilise les fonctions de Helpers.gs pour remplir les champs du formulaire en fonction du contenu de l'e-mail.
Remplacez le code par défaut dans Cards.gs par le code suivant :
var FIELDNAMES = ['Date', 'Amount', 'Description', 'Spreadsheet URL'];
/**
* Creates the main card users see with form inputs to log expenses.
* Form can be prefilled with values.
*
* @param {String[]} opt_prefills Default values for each input field.
* @param {String} opt_status Optional status displayed at top of card.
* @returns {Card}
*/
function createExpensesCard(opt_prefills, opt_status) {
var card = CardService.newCardBuilder();
card.setHeader(CardService.newCardHeader().setTitle('Log Your Expense'));
if (opt_status) {
if (opt_status.indexOf('Error: ') == 0) {
opt_status = '<font color=\'#FF0000\'>' + opt_status + '</font>';
} else {
opt_status = '<font color=\'#228B22\'>' + opt_status + '</font>';
}
var statusSection = CardService.newCardSection();
statusSection.addWidget(CardService.newTextParagraph()
.setText('<b>' + opt_status + '</b>'));
card.addSection(statusSection);
}
var formSection = createFormSection(CardService.newCardSection(),
FIELDNAMES, opt_prefills);
card.addSection(formSection);
return card;
}
/**
* Creates form section to be displayed on card.
*
* @param {CardSection} section The card section to which form items are added.
* @param {String[]} inputNames Names of titles for each input field.
* @param {String[]} opt_prefills Default values for each input field.
* @returns {CardSection}
*/
function createFormSection(section, inputNames, opt_prefills) {
for (var i = 0; i < inputNames.length; i++) {
var widget = CardService.newTextInput()
.setFieldName(inputNames[i])
.setTitle(inputNames[i]);
if (opt_prefills && opt_prefills[i]) {
widget.setValue(opt_prefills[i]);
}
section.addWidget(widget);
}
return section;
}
La fonction createExpensesCard accepte un tableau de valeurs pour préremplir le formulaire en tant qu'argument facultatif. La fonction peut afficher un message d'état facultatif, qui est rouge si l'état commence par "Error:" (Erreur :) et vert dans le cas contraire. Au lieu d'ajouter chaque champ au formulaire manuellement, une fonction d'assistance appelée createFormSection parcourt le processus de création de widgets de saisie de texte, définit chaque valeur par défaut avec setValue, puis ajoute les widgets à leurs sections respectives sur la fiche.
Remplacez maintenant le code par défaut dans Helpers.gs par ce code :
/**
* Finds largest dollar amount from email body.
* Returns null if no dollar amount is found.
*
* @param {Message} message An email message.
* @returns {String}
*/
function getLargestAmount(message) {
return 'TODO';
}
/**
* Determines date the email was received.
*
* @param {Message} message An email message.
* @returns {String}
*/
function getReceivedDate(message) {
return 'TODO';
}
/**
* Determines expense description by joining sender name and message subject.
*
* @param {Message} message An email message.
* @returns {String}
*/
function getExpenseDescription(message) {
return 'TODO';
}
/**
* Determines most recent spreadsheet URL.
* Returns null if no URL was previously submitted.
*
* @returns {String}
*/
function getSheetUrl() {
return 'TODO';
}
Les fonctions de Helpers.gs sont appelées par getContextualAddon pour déterminer les valeurs préremplies du formulaire. Pour l'instant, ces fonctions ne renverront que la chaîne "TODO", car vous implémenterez la logique de préremplissage lors d'une étape ultérieure.
Ensuite, mettez à jour le code dans GetContextualAddon.gs afin qu'il utilise le code dans Cards.gs et Helpers.gs. Remplacez le code dans GetContextualAddon.gs par le code suivant :
/**
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Returns the contextual add-on data that should be rendered for
* the current e-mail thread. This function satisfies the requirements of
* an 'onTriggerFunction' and is specified in the add-on's manifest.
*
* @param {Object} event Event containing the message ID and other context.
* @returns {Card[]}
*/
function getContextualAddOn(event) {
var message = getCurrentMessage(event);
var prefills = [getReceivedDate(message),
getLargestAmount(message),
getExpenseDescription(message),
getSheetUrl()];
var card = createExpensesCard(prefills);
return [card.build()];
}
/**
* Retrieves the current message given an action event object.
* @param {Event} event Action event object
* @return {Message}
*/
function getCurrentMessage(event) {
var accessToken = event.messageMetadata.accessToken;
var messageId = event.messageMetadata.messageId;
GmailApp.setCurrentMessageAccessToken(accessToken);
return GmailApp.getMessageById(messageId);
}
Notez la nouvelle fonction getCurrentMessage, qui utilise l'événement fourni par Gmail pour lire le message actuellement ouvert par l'utilisateur. Pour que cette fonction fonctionne, ajoutez un champ d'application supplémentaire au fichier manifeste du script, qui autorise l'accès en lecture seule aux messages Gmail.
Dans appscript.json, mettez à jour oauthScopes afin qu'il demande également le champ d'application https://www.googleapis.com/auth/gmail.addons.current.message.readonly.
"oauthScopes": [
"https://www.googleapis.com/auth/gmail.addons.execute",
"https://www.googleapis.com/auth/gmail.addons.current.message.readonly"
],
Dans Gmail, exécutez votre module complémentaire et autorisez Expense It! à consulter les e-mails. Les champs du formulaire sont désormais préremplis avec "TODO".
5. Interagir avec Google Sheets
Le module complémentaire Expense It! comporte un formulaire permettant à l'utilisateur de saisir des informations sur une dépense, mais ces informations ne sont pas transmises. Ajoutons un bouton qui envoie les données du formulaire à une feuille de calcul Google Sheets.
Pour ajouter un bouton, nous allons utiliser la classe ButtonSet. Pour interagir avec Google Sheets, nous allons utiliser le service Google Sheets.
Modifiez createFormSection pour renvoyer un bouton intitulé "Envoyer" dans la section du formulaire de la fiche. Suivez les instructions suivantes :
- Créez un bouton de texte à l'aide de
CardService.newTextButton(), en le nommant "Envoyer" à l'aide deCardService.TextButton.setText(). - Concevez le bouton de sorte que, lorsqu'il est cliqué, l'action
submitFormsuivante soit appelée viaCardService.TextButton.setOnClickAction():
/**
* Logs form inputs into a spreadsheet given by URL from form.
* Then displays edit card.
*
* @param {Event} e An event object containing form inputs and parameters.
* @returns {Card}
*/
function submitForm(e) {
var res = e['formInput'];
try {
FIELDNAMES.forEach(function(fieldName) {
if (! res[fieldName]) {
throw 'incomplete form';
}
});
var sheet = SpreadsheetApp
.openByUrl((res['Spreadsheet URL']))
.getActiveSheet();
sheet.appendRow(objToArray(res, FIELDNAMES.slice(0, FIELDNAMES.length - 1)));
return createExpensesCard(null, 'Logged expense successfully!').build();
}
catch (err) {
if (err == 'Exception: Invalid argument: url') {
err = 'Invalid URL';
res['Spreadsheet URL'] = null;
}
return createExpensesCard(objToArray(res, FIELDNAMES), 'Error: ' + err).build();
}
}
/**
* Returns an array corresponding to the given object and desired ordering of keys.
*
* @param {Object} obj Object whose values will be returned as an array.
* @param {String[]} keys An array of key names in the desired order.
* @returns {Object[]}
*/
function objToArray(obj, keys) {
return keys.map(function(key) {
return obj[key];
});
}
- Créez un widget d'ensemble de boutons à l'aide de
CardService.newButtonSet()et ajoutez votre bouton de texte à l'ensemble de boutons avecCardService.ButtonSet.addButton(). - Ajoutez le widget de l'ensemble de boutons à la section du formulaire de la fiche à l'aide de
CardService.CardSection.addWidget().
En quelques lignes de code, nous pouvons ouvrir une feuille de calcul à partir de son URL, puis ajouter une ligne de données à cette feuille. Notez que les entrées du formulaire sont transmises à la fonction dans le cadre de l'événement e, et nous vérifions que l'utilisateur a fourni tous les champs. En l'absence d'erreurs, nous créons une carte de dépenses vide avec un état favorable. Si nous détectons une erreur, nous renvoyons la carte remplie d'origine avec le message d'erreur. La fonction d'assistance objToArray facilite la conversion des réponses du formulaire en tableau, qui peut ensuite être ajouté à la feuille de calcul.
Enfin, mettez à jour la section oauthScopes dans appsscript.json et demandez à nouveau le champ d'application https://www.googleapis.com/auth/spreadsheets. Lorsqu'il est autorisé, ce champ d'application permet au module complémentaire de lire et de modifier les feuilles de calcul Google Sheets d'un utilisateur.
"oauthScopes": [
"https://www.googleapis.com/auth/gmail.addons.execute",
"https://www.googleapis.com/auth/gmail.addons.current.message.readonly",
"https://www.googleapis.com/auth/spreadsheets"
],
Si vous n'avez pas encore créé de feuille de calcul, faites-le sur https://docs.google.com/spreadsheets/.
Relancez le module complémentaire et essayez d'envoyer le formulaire. Assurez-vous de saisir l'URL complète de votre URL de destination dans le champ URL de la feuille de calcul du formulaire.
6. Stocker des valeurs avec le service Properties
Les utilisateurs enregistrent souvent de nombreuses dépenses dans la même feuille de calcul. Il serait donc pratique de proposer l'URL de la feuille de calcul la plus récente comme valeur par défaut dans la fiche. Pour connaître l'URL de la feuille de calcul la plus récente, nous devons stocker ces informations chaque fois que le module complémentaire est utilisé.
Le service Properties nous permet de stocker des paires clé/valeur. Dans notre cas, une clé raisonnable serait "SPREADSHEET_URL" et la valeur serait l'URL elle-même. Pour stocker une telle valeur, vous devez modifier submitForm dans Cards.gs afin que l'URL de la feuille de calcul soit stockée en tant que propriété lorsque vous ajoutez une ligne à la feuille.
Notez que les propriétés peuvent avoir l'une des trois portées suivantes : script, utilisateur ou document. Le champ d'application document ne s'applique pas aux modules complémentaires Gmail, mais il est pertinent pour un autre type de module complémentaire lors du stockage d'informations spécifiques à un document ou une feuille de calcul Google Docs. Pour notre module complémentaire, le comportement souhaité est qu'un utilisateur puisse voir sa propre feuille de calcul la plus récente (et non celle d'un autre utilisateur) comme option par défaut dans le formulaire. Par conséquent, nous sélectionnons la portée user au lieu de la portée script.
Utilisez PropertiesService.getUserProperties().setProperty() pour stocker l'URL de la feuille de calcul. Ajoutez le code suivant à submitForm dans Cards.gs :
PropertiesService.getUserProperties().setProperty('SPREADSHEET_URL',
res['Spreadsheet URL']);
Modifiez ensuite la fonction getSheetUrl dans Helpers.gs pour renvoyer la propriété stockée afin que l'utilisateur voie l'URL la plus récente chaque fois qu'il utilise le module complémentaire. Utilisez PropertiesService.getUserProperties().getProperty() pour obtenir la valeur de la propriété.
/**
* Determines most recent spreadsheet URL.
* Returns null if no URL was previously submitted.
*
* @returns {String}
*/
function getSheetUrl() {
return PropertiesService.getUserProperties().getProperty('SPREADSHEET_URL');
}
Enfin, pour accéder au service Property, le script devra également être autorisé. Ajoutez le champ d'application https://www.googleapis.com/auth/script.storage au fichier manifeste comme précédemment pour permettre à votre module complémentaire de lire et d'écrire des informations sur les propriétés.
7. Analyser le message Gmail
Pour vraiment faire gagner du temps aux utilisateurs, préremplissons le formulaire avec les informations pertinentes sur la dépense issues de l'e-mail. Nous avons déjà créé des fonctions dans Helpers.gs qui jouent ce rôle, mais jusqu'à présent, nous n'avons renvoyé que "TODO" pour la date, le montant et la description de la dépense.
Par exemple, nous pouvons obtenir la date de réception de l'e-mail et l'utiliser comme valeur par défaut pour la date de la dépense.
/**
* Determines date the email was received.
*
* @param {Message} message - The message currently open.
* @returns {String}
*/
function getReceivedDate(message) {
return message.getDate().toLocaleDateString();
}
Implémentez les deux fonctions restantes :
getExpenseDescriptionpeut impliquer de joindre le nom de l'expéditeur et l'objet du message, bien qu'il existe des moyens plus sophistiqués d'analyser le corps du message et de fournir une description encore plus précise.- Pour
getLargestAmount, essayez de rechercher des symboles spécifiques associés à l'argent. Les reçus comportent souvent plusieurs valeurs, comme les taxes et autres frais. Réfléchissez à la façon dont vous pourriez identifier le montant correct. Les expressions régulières peuvent également être utiles.
Si vous avez besoin d'inspiration, consultez la documentation de référence pour GmailMessage ou le code de la solution que vous avez téléchargé au début de l'atelier de programmation. Une fois que vous avez conçu vos propres implémentations pour toutes les fonctions de Helpers.gs, testez votre module complémentaire. Ouvrez vos reçus et commencez à les enregistrer dans une feuille de calcul.
8. Effacer le formulaire avec les actions de la carte
Que se passe-t-il si Expense It! identifie mal une dépense dans un e-mail ouvert et préremplit le formulaire avec des informations incorrectes ? L'utilisateur efface le formulaire. La classe CardAction nous permet de spécifier une fonction qui est appelée lorsque l'action est sélectionnée. Utilisons-le pour permettre à l'utilisateur d'effacer rapidement le formulaire.
Modifiez createExpensesCard de sorte que la fiche qu'elle renvoie comporte une action de fiche intitulée "Effacer le formulaire" et que, lorsque l'utilisateur clique dessus, elle appelle la fonction clearForm suivante, que vous pouvez coller dans Cards.gs. Vous devrez transmettre opt_status en tant que paramètre nommé "Status" à l'action pour vous assurer que le message d'état reste affiché lorsque le formulaire est effacé. N'oubliez pas que les paramètres facultatifs des actions doivent être de type Object.<string, string>. Par conséquent, si opt_status n'est pas disponible, vous devez transmettre {'Status' : ''}.
/**
* Recreates the main card without prefilled data.
*
* @param {Event} e An event object containing form inputs and parameters.
* @returns {Card}
*/
function clearForm(e) {
return createExpensesCard(null, e['parameters']['Status']).build();
}
9. Créer une feuille de calcul
En plus d'utiliser Google Apps Script pour modifier une feuille de calcul existante, vous pouvez créer une feuille de calcul entièrement nouvelle de manière programmatique. Pour notre module complémentaire, nous allons permettre à l'utilisateur de créer une feuille de calcul pour les dépenses. Pour commencer, ajoutez la section de carte suivante à la carte renvoyée par createExpensesCard.
var newSheetSection = CardService.newCardSection();
var sheetName = CardService.newTextInput()
.setFieldName('Sheet Name')
.setTitle('Sheet Name');
var createExpensesSheet = CardService.newAction()
.setFunctionName('createExpensesSheet');
var newSheetButton = CardService.newTextButton()
.setText('New Sheet')
.setOnClickAction(createExpensesSheet);
newSheetSection.addWidget(sheetName);
newSheetSection.addWidget(CardService.newButtonSet().addButton(newSheetButton));
card.addSection(newSheetSection);
Désormais, lorsque l'utilisateur clique sur le bouton "New Sheet" (Nouvelle feuille), le module complémentaire génère une feuille de calcul mise en forme avec une ligne d'en-tête figée pour qu'elle soit toujours visible. L'utilisateur spécifie un titre pour la nouvelle feuille de calcul dans le formulaire, mais il peut être judicieux d'inclure une valeur par défaut au cas où le formulaire serait vide. Dans votre implémentation de createExpensesSheet, renvoyez une fiche presque identique à la fiche existante, en ajoutant un message d'état approprié et en préremplissant le champ d'URL avec l'URL de la nouvelle feuille de calcul.
10. Félicitations !
Vous avez conçu et implémenté un module complémentaire Gmail qui trouve une dépense dans un e-mail et aide les utilisateurs à l'enregistrer dans une feuille de calcul en quelques secondes. Vous avez utilisé Google Apps Script pour interagir avec plusieurs API Google et conserver les données entre plusieurs exécutions du module complémentaire.
Améliorations possibles
Laissez libre cours à votre imagination pour améliorer Expense It!. Voici quelques idées pour rendre le produit encore plus utile :
- Lien vers la feuille de calcul une fois que l'utilisateur a enregistré une dépense
- Ajouter la possibilité de modifier/annuler l'enregistrement d'une dépense
- Intégrez des API externes pour permettre aux utilisateurs d'effectuer des paiements et de demander de l'argent.