Copy and paste these professional scripts, configured step-by-step, to automate Google Sheets, Gmail, Drive, and Agenda instantly.
💡 How to install these scripts in your document?
1
In your Google Sheet, click Extensions > Apps Script.
2
Delete any existing code and paste one of our examples below.
3
Customize the parameters (such as folder IDs), save, and click Run.
Select an automation to install:
Automated Unpaid Invoice Email Reminders
Sheets + Gmail
Loops through your billing rows in Google Sheets, identifies clients with "Unpaid" status, sends a professional, customized HTML email reminder, and writes the sent date in the sheet to prevent duplicates.
Document configuration:
Recommended columns structure:
Column A (index 0): Client name (e.g. ABC Company)
Column B (index 1): Client email address
Column C (index 2): Invoice amount (e.g. 1500)
Column D (index 3): Due date (Date format)
Column E (index 4): Invoice status (type Non Payé or Unpaid to trigger the reminder)
Column F (index 5): Reminder status (leave blank, the script will write Sent on DD/MM/YYYY HH:MM)
Setup instructions:
Set up your Google Sheet with the column structure above.
Add a few test rows using your own email address in column B.
Paste the code on the right into the Google Apps Script editor.
Click Run. On the first launch, authorize the required security permissions (see auth steps below if needed).
relance_impayes.gs
function relancerFacturesImpayees() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const dataRange = sheet.getDataRange();
const values = dataRange.getValues();
// Index des colonnes (0 = Colonne A, 1 = B, etc.)
const COL_NOM = 0;
const COL_EMAIL = 1;
const COL_MONTANT = 2;
const COL_ECHEANCE = 3;
const COL_STATUT = 4;
const COL_RAPPEL = 5;
let rappelsEnvoyes = 0;
// Parcourir à partir de la ligne 2 (index 1) pour ignorer les en-têtes
for (let i = 1; i < values.length; i++) {
const row = values[i];
const nom = row[COL_NOM];
const email = row[COL_EMAIL];
const montant = row[COL_MONTANT];
const echeance = row[COL_ECHEANCE];
const statut = row[COL_STATUT];
const rappelEnvoye = row[COL_RAPPEL];
// Vérifier si la facture n'est pas payée et qu'aucun rappel n'a été envoyé
if ((statut === "Non Payé" || statut === "Unpaid") && !rappelEnvoye) {
if (email && email.indexOf("@") !== -1) {
// Formater la date d'échéance
const dateFormatee = echeance instanceof Date ? Utilities.formatDate(echeance, Session.getScriptTimeZone(), "dd/MM/yyyy") : echeance;
// Template d'e-mail HTML personnalisé
const sujet = "Rappel de paiement : Facture en attente - " + nom;
const corpsHtml = `
Rappel de paiement
Bonjour ${nom},
Sauf erreur de notre part, le règlement de votre facture d'un montant de ${montant} €, qui était attendu pour le ${dateFormatee}, ne nous est pas parvenu.
Nous vous demandons de bien vouloir régulariser cette situation dans les plus brefs délais.
Ceci est un message automatique envoyé depuis notre gestionnaire de facturation.
`;
try {
// Envoyer l'email
MailApp.sendEmail({
to: email,
subject: sujet,
htmlBody: corpsHtml
});
// Mettre à jour le statut du rappel dans la feuille de calcul
const dateAujourdhui = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "dd/MM/yyyy HH:mm");
sheet.getRange(i + 1, COL_RAPPEL + 1).setValue("Envoyé le " + dateAujourdhui);
rappelsEnvoyes++;
Logger.log("Rappel envoyé à " + nom + " (" + email + ")");
} catch (erreur) {
Logger.log("Erreur lors de l'envoi de l'e-mail à " + email + " : " + erreur.message);
}
}
}
}
Browser.msgBox("Traitement terminé : " + rappelsEnvoyes + " rappel(s) envoyé(s).");
}
Invoice PDF Generator & Drive Storage
Sheets + Docs + Drive
Generates custom PDF invoices from your Google Sheets data. The script duplicates a Google Doc model, replaces placeholders like {{ClientNom}} with the table values, saves the generated PDF to a specific Drive folder, and writes the PDF URL back to your sheet.
Document configuration:
Recommended columns structure:
Column A (index 0): Invoice number (e.g. INV-2026-001)
Column B (index 1): Client name (replaces {{ClientNom}})
Column C (index 2): Client address (replaces {{ClientAdresse}})
Column D (index 3): Service description (replaces {{Description}})
Column E (index 4): Total amount (replaces {{MontantTotal}})
Column F (index 5): PDF Link (leave blank, the script will automatically write the Google Drive file URL here)
Setup instructions:
Create a Google Doc model with exact placeholder tags (e.g., {{FactureNumero}}, {{ClientNom}}, {{ClientAdresse}}, {{Description}}, {{MontantTotal}}, {{DateEmission}}). Copy the ID of this template document (from the URL between /d/ and /edit).
Create a new folder in Google Drive for PDF storage and copy its ID (from the URL after /folders/).
In the Apps Script code on the right, replace the values of the constants TEMPLATE_DOC_ID and DOSSIER_PDF_ID (lines 11 and 12) with your copied IDs.
Fill in a row in your sheet, leaving Column F empty, and run the script. The PDF will be generated and its link will appear in your cell!
generate_pdf.gs
// ⚠️ À MODIFIER : L'identifiant du modèle Google Doc et du dossier Drive de destination
const TEMPLATE_DOC_ID = "VOTRE_ID_DE_DOCUMENT_TEMPLATE";
const DOSSIER_PDF_ID = "VOTRE_ID_DE_DOSSIER_DRIVE";
function genererFacturesPDF() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const values = sheet.getDataRange().getValues();
// Index des colonnes (0 = A, 1 = B, etc.)
const COL_NUMERO = 0;
const COL_NOM = 1;
const COL_ADRESSE = 2;
const COL_DESCRIPTION = 3;
const COL_MONTANT = 4;
const COL_LIEN_PDF = 5;
const templateDoc = DriveApp.getFileById(TEMPLATE_DOC_ID);
const dossierDestination = DriveApp.getFolderById(DOSSIER_PDF_ID);
let documentsGeneres = 0;
// Parcourir les lignes du tableau (ignorer l'en-tête)
for (let i = 1; i < values.length; i++) {
const row = values[i];
const numero = row[COL_NUMERO];
const nom = row[COL_NOM];
const adresse = row[COL_ADRESSE];
const description = row[COL_DESCRIPTION];
const montant = row[COL_MONTANT];
const lienPdfExistante = row[COL_LIEN_PDF];
// Générer uniquement si le numéro de facture existe et qu'il n'y a pas encore de PDF généré
if (numero && !lienPdfExistante) {
// 1. Créer une copie temporaire du Google Doc dans le dossier de destination
const nomFichierTemp = "Facture_" + numero + "_Temp";
const fichierTemp = templateDoc.makeCopy(nomFichierTemp, dossierDestination);
const docTempId = fichierTemp.getId();
const docTempInstance = DocumentApp.openById(docTempId);
const body = docTempInstance.getBody();
// 2. Remplacer les balises textuelles dans le modèle de document
body.replaceText("{{FactureNumero}}", numero);
body.replaceText("{{ClientNom}}", nom || "");
body.replaceText("{{ClientAdresse}}", adresse || "");
body.replaceText("{{Description}}", description || "");
body.replaceText("{{MontantTotal}}", parseFloat(montant || 0).toFixed(2) + " €");
body.replaceText("{{DateEmission}}", Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "dd/MM/yyyy"));
// Enregistrer et fermer la copie temporaire
docTempInstance.saveAndClose();
// 3. Convertir le document temporaire en PDF
const pdfBlob = fichierTemp.getAs(MimeType.PDF);
pdfBlob.setName("Facture_" + numero + "_" + (nom || "Client") + ".pdf");
// 4. Enregistrer le fichier PDF définitif et récupérer son URL publique
const fichierPdf = dossierDestination.createFile(pdfBlob);
const lienPdf = fichierPdf.getUrl();
// 5. Supprimer le fichier temporaire Google Doc
fichierTemp.setTrashed(true);
// 6. Écrire le lien du PDF dans le tableau Sheets
sheet.getRange(i + 1, COL_LIEN_PDF + 1).setValue(lienPdf);
documentsGeneres++;
Logger.log("Facture PDF générée pour " + nom + " : " + lienPdf);
}
}
Browser.msgBox("Succès : " + documentsGeneres + " facture(s) PDF générée(s).");
}
Google Calendar Event Scheduler
Sheets + Calendar
Reads meetings or tasks from Google Sheets and automatically creates the corresponding events in your primary Google Calendar. It invites the client if their email is provided and stores the event ID to prevent duplicate creations on subsequent runs.
Document configuration:
Recommended columns structure:
Column A (index 0): Event Title (e.g., Client Meeting)
Column B (index 1): Start date & time (e.g., 06/04/2026 14:00:00)
Column C (index 2): End date & time (e.g., 06/04/2026 15:30:00)
Column D (index 3): Event description or notes
Column E (index 4): Guest email (optional, will receive an agenda invite)
Column F (index 5): Google Calendar Event ID (leave blank, the script will write the unique event ID here)
Setup instructions:
Set up your Google Sheet with the structure above and format columns B and C as "Date and time" in Sheets (Format > Number > Date time).
Paste the code on the right into Apps Script.
Select the function synchroniserCalendrier and click Run.
Open your Google Calendar: your events have been added and IDs have been written back to column F.
sync_calendar.gs
function synchroniserCalendrier() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const values = sheet.getDataRange().getValues();
// Index des colonnes (0 = A, 1 = B, etc.)
const COL_TITRE = 0;
const COL_DEBUT = 1;
const COL_FIN = 2;
const COL_DESC = 3;
const COL_INVITE = 4;
const COL_EVENT_ID = 5;
// Récupérer l'agenda principal de l'utilisateur
const calendar = CalendarApp.getDefaultCalendar();
let evenementsCrees = 0;
// Parcourir à partir de la ligne 2
for (let i = 1; i < values.length; i++) {
const row = values[i];
const titre = row[COL_TITRE];
const dateDebut = row[COL_DEBUT];
const dateFin = row[COL_FIN];
const description = row[COL_DESC];
const emailInvite = row[COL_INVITE];
const eventIdExistant = row[COL_EVENT_ID];
// Créer uniquement si le titre et les dates sont valides, et s'il n'y a pas d'ID déjà stocké (doublons)
if (titre && dateDebut instanceof Date && dateFin instanceof Date && !eventIdExistant) {
try {
// Options de création de l'événement (description + invité)
const options = {
description: description || ""
};
// Ajouter un invité si présent
if (emailInvite && emailInvite.indexOf("@") !== -1) {
options.guests = emailInvite;
options.sendInvites = true;
}
// Créer l'événement dans Google Calendar
const event = calendar.createEvent(titre, dateDebut, dateFin, options);
const eventId = event.getId();
// Enregistrer l'identifiant de l'événement dans Sheets pour éviter de le recréer
sheet.getRange(i + 1, COL_EVENT_ID + 1).setValue(eventId);
evenementsCrees++;
Logger.log("Événement créé : " + titre + " (ID: " + eventId + ")");
} catch (erreur) {
Logger.log("Erreur lors de la création de l'événement '" + titre + "' : " + erreur.message);
}
}
}
Browser.msgBox("Terminé : " + evenementsCrees + " événement(s) ajouté(s) à votre agenda.");
}
🛡️ Google Security Alert: Don't panic!
When you run a script for the very first time, Google displays a large red warning panel stating that the application isn't verified. This is a standard protection for all privately created scripts. Here is how to pass it safely:
1. Advanced Settings
On the warning panel, click on the Advanced link at the bottom left.
2. Force Access
Then, click on the small link Go to [Your Project Name] (unsafe). Don't worry, the script remains private and runs only inside your space.
3. Allow Access
A window summarizes what the script will access (read Sheets, send emails). Click Allow. Your data remains secure and private inside your Google account.