Envoyer un devis par Gmail depuis Sheet, grâce à Apps Script
Ce code se décompose en deux blocs. Avec Javascript, on récupère les informations des produits sélectionnés dans le tableur, puis on créer le devis en copiant une feuille modèle se trouvant dans le Drive, et en y substituant les données qui nous intéressant. Enfin, envoyer le mail contenant le devis en pièce jointe. Le seconde bloc est le corps du mail en HTML, avec une suggestion de présentation façon Webmarketing.
Attention, je suis un codeur débutant. Il y a certainement des façons beaucoup plus élégantes, mais pour moi, ça fonctionne. Je me suis même ajouté un Bouton déclencheur dans le Sheet, et BAM, c'est fait ! De toute façon, vous devrez vous taper la tête contre le mur pour comprendre, tester, personnaliser, etc... Bon courage !
//copier la feuille de base dans le répertoire pour un nouveau devis
function Envoyer (){
//Créer une copie du Devis depuis le modèle, et le placer dans un dossier.
var idModele ="TAFEUILLEMODELE";
var dossierDestination=DriveApp.getFolderById("TONDOSSIER");
var ledevis = DriveApp.getFileById(idModele).makeCopy(dossierDestination);
//date
var date = new Date();
var chrono = Utilities.formatDate(date, 'Europe/Paris', 'yyyy-MM-dd');
//renommer le fichier
ledevis.setName(chrono + ' Devis Location de Matériel Audiovisuel | Dorian Clair');
//importer les infos clients du google sheet
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet=ss.getSheetByName('Panier');
var pretotal = sheet.getRange('G2').getValue();
var total= pretotal.toFixed(2)
var jours=sheet.getRange('E2').getValue();
var projet= sheet.getRange('J5').getValue();
var responsable= sheet.getRange('J8').getValue();
var mail= sheet.getRange('J11').getValue();
var telephone= sheet.getRange('K11').getValue();
var production = sheet.getRange('K5').getValue();
var adresse = sheet.getRange('K8').getValue();
var precaution = sheet.getRange('J2').getValue();
var caution = precaution.toFixed(2); //Tofixed arrondie à la deuxième décimale
//Recalcul des dates.
const MILLIS_PER_DAY = 1000 * 60 * 60 * 24;
var premierjour = sheet.getRange('J14').getValue();
var debut = Utilities.formatDate(premierjour, 'Europe/Paris', 'yyyy-MM-dd');
var enleve = new Date(premierjour.getTime() - 24 * 60 * 60 * 1000);
var enlevement = Utilities.formatDate(enleve, 'Europe/Paris', 'yyyy-MM-dd');
var ret = new Date(premierjour.getTime() + jours * 24 * 60 * 60 * 1000);
var retour = Utilities.formatDate(ret, 'Europe/Paris', 'yyyy-MM-dd');
var der = new Date(premierjour.getTime() + jours * 24 * 60 * 60 * 1000 - 24 * 60 * 60 * 1000);
var fin = Utilities.formatDate(der, 'Europe/Paris', 'yyyy-MM-dd');
var annul = new Date(premierjour.getTime() - 3* 24 * 60 * 60 * 1000);
var annulation = Utilities.formatDate(annul, 'Europe/Paris', 'yyyy-MM-dd');
//modifier le devis, d'après les infos.
var doc = DocumentApp.openById(ledevis.getId());
var body = doc.getBody();
body.replaceText('{Projet}',projet);
body.replaceText('{Nom}', responsable);
body.replaceText('{Mail}', mail);
body.replaceText('{Telephone}', telephone);
body.replaceText('{Total}', total);
body.replaceText('{Jours}', jours);
body.replaceText('{Now}', chrono);
body.replaceText('{Production}', production);
body.replaceText('{Adresse}', adresse);
body.replaceText('{Enlèvement}', enlevement);
body.replaceText('{Retour}', retour);
body.replaceText('{Debut}', debut);
body.replaceText('{Fin}', fin);
body.replaceText('{Annulation}',annulation);
body.replaceText('{Caution}',caution);
//inserer le tableau du Panier
var rgel=body.findText('{Panier}');
var element=rgel.getElement();
var childIndex=body.getChildIndex(element.getParent());
body.getChild(childIndex).asText().setText('');
var derniereligne = sheet.getLastRow();
var rowsData = sheet.getRange(3,2,derniereligne,6 ).getDisplayValues(); //
table = body.insertTable(childIndex,rowsData)
//changer la gueule du panier
table.editAsText().setFontSize(8);
table.setColumnWidth(0,160);
table.setColumnWidth(1,80);
table.setColumnWidth(2,60);
table.setColumnWidth(3,60);
table.setColumnWidth(4,40);
table.setColumnWidth(5,40);
table.getRow(0).editAsText().setBold(true).setFontSize(10);
//Supprimer les lignes vides
var numberOfRows = table.getNumRows()
for (var rowIndex = 0; rowIndex < numberOfRows; rowIndex++) {
var nextRow = table.getRow(rowIndex)
var numberOfColumns = nextRow.getNumCells()
// A row is assumed empty until proved otherwise
var foundEmptyRow = true
for (var columnIndex = 0; columnIndex < numberOfColumns; columnIndex++) {
if (nextRow.getCell(columnIndex).getText() !== '') {
foundEmptyRow = false
break
}
} // for each column
if (foundEmptyRow) {
table.removeRow(rowIndex)
numberOfRows--
rowIndex--
}
} // For each row
//Injecter les liens SKU
for (var rowIndex = 1; rowIndex < numberOfRows; rowIndex++) { //Une boucle pour passer de ligne à ligne
const row = rowIndex;
const column = 2;
const sku = table.getCell(row, column - 1).getText(); // Récupération du Sku dans le document
if (sku ==''){ continue; } //Antibug, si la cell est vide, recommencer une itération.
var findText = sku;
var trouver = sheet.createTextFinder(findText).matchEntireCell(true).findNext(); //Retrouver le sku dans le sheet
var lerow = trouver.getRow(); //extraire sa cell
var laColumn = trouver.getColumn();
var link = sheet.getRange(lerow,laColumn).getRichTextValue().getLinkUrl(); // Récupérer le lien.
table.getCell(row, column - 1).setLinkUrl(link); //Inserer ce lien dans le devis.
}
//Du zèle pour le pied de page
var footer = doc.getFooter();
footer.replaceText('{Projet}',projet);
//Envoyer le mail
var emailAddress = sheet.getRange('J11').getValue() +", TOI@gmail.com";
var devisurl = doc.getUrl();
//var subject = projet + " Devis du matériel" ;
//var message= ["Bonjour, Pour votre prochain film, voici l'outillage sélectionné ! " + devisurl + " Je vous recontacterai, pour discuter du projet :) En vous souhaitant des images, Dorian Clair"] ;
//var alias = GmailApp.getAliases();
//GmailApp.sendEmail(emailAddress,subject,message,{from: alias[3], name:"Dorian Clair", });
var subject = projet + "📜 Devis du matériel" ;
const temp = HtmlService.createTemplateFromFile('Matériel');
const message = temp.evaluate().getContent();
var urlhtml = ('<!DOCTYPE html><html> <a href ="') // C'est de la triche pour ingérer l'url du devis dans le htlm.
var finhtml = ('"<div style="text-align:center"> <p style="margin:0;font-size:35px;line-height:90px" class="m_-6753985970031334258jb m_-6753985970031334258nb">🎬</a> </span> </p> </div> </html>')
MailApp.sendEmail({
from: "TOI@gmail.com",
to: emailAddress,
name: "Dorian Clair",
replyTo: "TOI@gmail.com"
, subject
, htmlBody: message+urlhtml+devisurl+finhtml
});
//Accès public, en mode lecteur.
var id = doc.getId();
var file = DriveApp.getFileById(id)
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW)
//Cleaner certaines cells.
//sheet.getRange('K14').setValue(0);
sheet.getRange('J5').setValue("");
sheet.getRange('K5').setValue("");
sheet.getRange('J8').setValue("");
sheet.getRange('K8').setValue("");
sheet.getRange('K11').setValue("");
//petite box pour annoncer la réussite du process
var ui = SpreadsheetApp.getUi();
ui.alert('Direction votre boite mail !');
}
//"Creator":"Dorian Clair | www.dorianclair.fr",
//"Published":"2022/07/07",
//"Modified":"2022/08/31"
Le Corps du Mail
Toujours dans Apps Script, ajoutez un fichier HTML
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<base target="_top">
</head>
<body>
<!-- mail à cliquer -->
<div class="">
<div class="aHl"></div>
<div id=":25v" tabindex="-1"></div>
<div id=":28d" class="ii gt" jslog="20277; u014N:xr6bB; 4:W251bGwsbnVsbCxbXV0.">
<div id=":286" class="a3s aiL msg-6753985970031334258"><u></u>
<div style="word-spacing:normal;background-color:#ffa200">
<div style="display:none;font-size:1px;color:#ffa200;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden"><span> La qualité à prix honnête. </span><span> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> </span></div>
style="display:table;height:1px!important;width:1px!important;border:0!important;margin:0!important;padding:0!important" width="1" height="1" border="0" class="CToWUd">
<div style="color:transparent;opacity:0;font-size:0px;border:0;max-height:1px;width:1px;margin:0px;padding:0px;border-width:0px!important;display:none!important;line-height:0px!important"></div>
<!--arrière Plan-->
<table align="center" class="m_-6753985970031334258html-emails-background" background="https://ci4.googleusercontent.com/proxy/qPVjLhy_Zfv-nBpw_ktTHGx7mLmKh_dvRduAQZ-4uBPYsaQ96gF3VWCEyAbvMvYCnBp2zlt-wLMOzV2jGRcwExwVGKr3n_Czhn3Dyqnz10qSSYaNpUAYXXhA1hUbn_nvbw=s0-d-e1-ft#https://static.wixstatic.com/media/a306cb_9d0d440596cb4243bede836841edcbe1.png"
border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#030303 url('https://ci4.googleusercontent.com/proxy/qPVjLhy_Zfv-nBpw_ktTHGx7mLmKh_dvRduAQZ-4uBPYsaQ96gF3VWCEyAbvMvYCnBp2zlt-wLMOzV2jGRcwExwVGKr3n_Czhn3Dyqnz10qSSYaNpUAYXXhA1hUbn_nvbw=s0-d-e1-ft#https://static.wixstatic.com/media/a306cb_9d0d440596cb4243bede836841edcbe1.png') center top/auto repeat;background-position:center top;background-repeat:repeat;background-size:auto;width:100%">
<td>
<!--ligne du haut-->
<div style="margin:20px auto;max-width:732px">
<div style="line-height:0;font-size:0">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%">
<tbody>
<tr>
<td style="direction:ltr;font-size:0px;padding:16px;text-align:center">
<div style="margin:0px auto;max-width:700px">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%">
<tbody>
<tr>
<td style="border-left:solid 2px #fff8e0;border-right:solid 2px #fff8e0;border-top:solid 2px #fff8e0;direction:ltr;font-size:0px;padding:0;text-align:center">
<div style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top" width="100%">
<tbody>
<tr>
<td class="m_-6753985970031334258oc" style="background:#000000;font-size:0px;word-break:break-word">
<div style="height:15px;line-height:15px"> </div>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!--Titre-->
<div class="m_-6753985970031334258k m_-6753985970031334258eb m_-6753985970031334258bb m_-6753985970031334258db m_-6753985970031334258cb m_-6753985970031334258pc" style="text-transform:uppercase;color:#ffffff;text-align:center;font-family:montserrat,Helvetica,sans-serif;letter-spacing:2px;line-height:1.3;font-size:44px;background:#000000;background-color:#000000;margin:0px auto;max-width:700px">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#000000;background-color:#000000;width:100%">
<tbody>
<tr>
<td style="border-left:solid 2px #fff8e0;border-right:solid 2px #fff8e0;direction:ltr;font-size:0px;padding:0px 0px 0px 0px;text-align:center">
<div class="m_-6753985970031334258v" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
<tbody>
<tr>
<td style="background-color:transparent;border:0px none transparent;border-radius:0px;vertical-align:top;padding:18px 30px 10px 30px">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
<tbody>
<tr>
<td align="left" class="m_-6753985970031334258rb" style="font-size:0px;padding:0px;word-break:break-word">
<div style="font-family:montserrat,Helvetica,sans-serif;font-size:44px;font-style:normal;line-height:1;text-align:left;text-transform:uppercase;color:#ffffff">
<div style="text-align:center">
<p style="margin:0;font-size:35px;line-height:57px" class="m_-6753985970031334258jb m_-6753985970031334258nb"><span style="font-family:montserrat,Helvetica,sans-serif;color:#ffffff" class="m_-6753985970031334258jb m_-6753985970031334258nb">Matériel Audiovisuel</span></p>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!--Image dessous -->
<div class="m_-6753985970031334258eb m_-6753985970031334258bb m_-6753985970031334258db m_-6753985970031334258cb" style="background:#000000;background-color:#000000;margin:0px auto;max-width:700px">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#000000;background-color:#000000;width:100%">
<tbody>
<tr>
<td style="border-left:solid 2px #fff8e0;border-right:solid 2px #fff8e0;direction:ltr;font-size:0px;padding:0px 0px 0px 0px;text-align:center">
<div class="m_-6753985970031334258v" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
<tbody>
<tr>
<td style="vertical-align:top;padding:0px 30px 0px 30px">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
<tbody>
<tr>
<td align="center" style="font-size:0px;padding:0px;word-break:break-word">
<div style="font-family:Ubuntu,Helvetica,Arial,sans-serif;font-size:13px;line-height:1;text-align:center;color:#000000">
<a style="display:inline-block;max-width:100%;line-height:40px"><img src="https://images.wixstatic.com/media/a306cb_dcbe4514b752457a8667df748d5e94c3~mv2.png/v1/fit/al_c,h_14,q_100,w_84,br_0,sat_-88,hue_-134/a306cb_dcbe4514b752457a8667df748d5e94c3~mv2.png"
width="84" height="14" style="display:inline-block;max-width:100%;width:auto;vertical-align:middle" alt="" class="CToWUd"></a>
</div>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!--Texte -->
<div class="m_-6753985970031334258h" style="color:#ffffff;font-family:helvetica,sans-serif;line-height:1.6;font-size:15.9px;background:#000000;background-color:#000000;margin:0px auto;max-width:700px">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#000000;background-color:#000000;width:100%">
<tbody>
<tr>
<td style="border-left:solid 2px #fff8e0;border-right:solid 2px #fff8e0;direction:ltr;font-size:0px;padding:0px 0px 0px 0px;text-align:center">
<div class="m_-6753985970031334258v" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
<tbody>
<tr>
<td style="background-color:transparent;border:0px none transparent;border-radius:0px;vertical-align:top;padding:18px 30px 18px 30px">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
<tbody>
<tr>
<td align="left" class="m_-6753985970031334258rb" style="font-size:0px;padding:0px;word-break:break-word">
<div style="font-family:Garamond;font-size:20.9px;font-style:normal;line-height:1;text-align:left;text-transform:none;color:#ffffff">
<div style="text-align:center">
<p style="margin:0;line-height:24px;font-size:20px" class="m_-6753985970031334258lb m_-6753985970031334258pb"><span style="color:#ffffff" class="m_-6753985970031334258lb m_-6753985970031334258pb">Bonjour, </span></p>
<p style="margin:0;line-height:24px;font-size:15px"
class="m_-6753985970031334258lb m_-6753985970031334258pb"> </p>
<p style="margin:0;line-height:24px;font-size:20px" class="m_-6753985970031334258lb m_-6753985970031334258pb"><span style="font-family:Garamond; color:#ffffff "class="m_-6753985970031334258lb m_-6753985970031334258pb">Pour votre prochain film, vous trouverez l'outillage en clapant !</span></p>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!--Site internet -->
<div class="m_-6753985970031334258eb m_-6753985970031334258bb m_-6753985970031334258db m_-6753985970031334258cb m_-6753985970031334258bc" style="background: #000000; background-color: #000000; margin: 0px auto; max-width: 700px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background: #000000; background-color: #000000; width: 100%;">
<tbody>
<tr>
<td style="border-left: solid 2px #fff8e0; border-right: solid 2px #fff8e0; direction: ltr; font-size: 0px; padding: 0px 0px 0px 0px; text-align: center;">
<div class="m_-6753985970031334258gc" style="height: 0px; padding: 10px 30px 0px 30px;"></div>
<div class="m_-6753985970031334258fc m_-6753985970031334258sc m_-6753985970031334258sb" style="font-size: 0px; text-align: left; direction: ltr; display: inline-block; vertical-align: middle; width: 100%; max-width: 1000%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
<tbody>
<tr>
<td style="vertical-align: middle; padding-right: 30px; padding-left: 0px;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
<tbody>
<tr>
<td align="center" class="m_-6753985970031334258dc" style="font-size: 0px; padding: 0px 10px 0px 10px; word-break: break-word;">
<div style="font-family: Ubuntu,Helvetica,Arial,sans-serif; font-size: 13px; line-height: 1; text-align: right; color: #000000;">
<div style="display: inline-block; vertical-align: right;"><a href="https://www.dorianclair.fr/location" style="text-decoration: none;" target="_blank" data-saferedirecturl="https://www.dorianclair.fr/acteur" rel="noopener"><span class="m_-6753985970031334258i" style="font-family: helvetica,sans-serif; font-size: 12px; margin-right: 1.5px; line-height: 25px; display: inline-block; overflow: hidden; vertical-align: middle; color: #ffffff; max-height: 100px;"><strong style="font-weight: inherit;">Retour au site</strong></span> <img height="22" src="https://images.wixstatic.com/media/b49ee3_dd9b1a8812ae41138409a667954a6088~mv2.png/v1/fit/w_750,h_750,br_0,sat_-100,hue_180,lg_0/b49ee3_dd9b1a8812ae41138409a667954a6088~mv2.png" style="vertical-align: middle; border: 0;" width="22" alt="" class="CToWUd" /></a></div>
</div>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
<div class="m_-6753985970031334258gc" style="height: 10px; padding: 0px 30px 0px 30px;"></div>
</td>
</tr>
</tbody>
</table>
</div>
<!--Ligne du Bas -->
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 100%;">
<tbody>
<tr>
<td style="border-bottom: solid 2px #fff8e0; border-left: solid 2px #fff8e0; border-right: solid 2px #fff8e0; direction: ltr; font-size: 0px; padding: 0; text-align: center;">
<div style="font-size: 0px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align: top;" width="100%">
<tbody>
<tr>
<td style="background: #000000; font-size: 0px; word-break: break-word;">
<div style="height: 10px; line-height: 10px;">
</div>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
Dorian Clair 2023-01-07