Een goedkeuringsflow bouwen met Apps Script, Forms en Gmail is een uitstekend voorbeeld van een complete bedrijfsautomatisering. Een medewerker dient een aanvraag in, de leidinggevende krijgt een mail met een goedkeur- en een afwijsknop, en de uitkomst wordt automatisch vastgelegd en teruggekoppeld. In dit artikel zet je zo'n flow stap voor stap op.
De opzet van de flow
De flow bestaat uit vier delen. Eerst dient iemand een aanvraag in via een Google Form. Die inzending logt in een spreadsheet met een unieke id en de status In behandeling. Daarna krijgt de goedkeurder een mail met twee links die naar jouw webapp wijzen, een voor goedkeuren en een voor afwijzen. Wanneer de goedkeurder klikt, werkt de webapp de status bij en mailt de aanvrager de uitkomst.
Webapp als beslispunt
De goedkeur- en afwijslinks wijzen naar je doGet-webapp met parameters, bijvoorbeeld ?id=123&actie=goedkeuren. De webapp leest die parameters via e.parameter. De referentie staat op developers.google.com/apps-script/guides/web.
Stap 1: de aanvraag loggen
Met een onFormSubmit-trigger schrijf je elke aanvraag naar een blad met een unieke id:
function bijAanvraag(e) {
const blad = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Aanvragen');
const response = e.response;
const aanvrager = response.getRespondentEmail();
const items = response.getItemResponses();
const omschrijving = items[0].getResponse();
const id = Utilities.getUuid();
blad.appendRow([id, aanvrager, omschrijving, 'In behandeling', new Date()]);
stuurNaarGoedkeurder(id, aanvrager, omschrijving);
}
Utilities.getUuid() geeft een unieke id zodat elke aanvraag eenduidig terug te vinden is. Koppel de bijAanvraag-functie aan een installeerbare onFormSubmit-trigger, niet aan een simpele trigger, want alleen een installeerbare trigger mag mailen en de respondent uitlezen.
Stap 2: de goedkeurder mailen met beslislinks
Bouw twee links naar je webapp en stuur ze naar de goedkeurder:
function stuurNaarGoedkeurder(id, aanvrager, omschrijving) {
const basis = 'jouw-webapp-url';
const goedkeuren = basis + '?id=' + id + '&actie=goedkeuren';
const afwijzen = basis + '?id=' + id + '&actie=afwijzen';
const html = '<p>Nieuwe aanvraag van ' + aanvrager + ':</p>' +
'<p>' + omschrijving + '</p>' +
'<p><a href="' + goedkeuren + '">Goedkeuren</a> | ' +
'<a href="' + afwijzen + '">Afwijzen</a></p>';
GmailApp.sendEmail('manager@cloud-captains.com', 'Aanvraag ter goedkeuring',
'Open in HTML.', { htmlBody: html });
}
De webapp-URL krijg je pas na de eerste deploy (zie stap 3 en het stappenplan). Zet hem daarna in basis.
Stap 3: de beslissing verwerken in de webapp
De webapp leest de parameters, controleert ze en werkt de status bij:
function doGet(e) {
const id = e.parameter.id;
const actie = e.parameter.actie;
if (!id || (actie !== 'goedkeuren' && actie !== 'afwijzen')) {
return HtmlService.createHtmlOutput('Ongeldige aanvraag.');
}
const blad = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Aanvragen');
const data = blad.getDataRange().getValues();
for (let i = 1; i < data.length; i++) {
if (data[i][0] === id) {
if (data[i][3] !== 'In behandeling') {
return HtmlService.createHtmlOutput('Deze aanvraag is al verwerkt.');
}
const status = actie === 'goedkeuren' ? 'Goedgekeurd' : 'Afgewezen';
blad.getRange(i + 1, 4).setValue(status);
informeerAanvrager(data[i][1], status);
return HtmlService.createHtmlOutput('Aanvraag ' + status.toLowerCase() + '.');
}
}
return HtmlService.createHtmlOutput('Aanvraag niet gevonden.');
}
Valideer elke klik streng
Controleer in de webapp altijd of de aanvraag nog In behandeling is voordat je de status wijzigt. Zonder die check kan iemand een oude link hergebruiken of een al verwerkte aanvraag opnieuw veranderen. Valideer de parameters streng en sta alleen geldige acties toe, want de webapp-URL is gokbaar.
Stap 4: de aanvrager terugkoppelen
Tot slot informeer je de aanvrager over de uitkomst:
function informeerAanvrager(email, status) {
MailApp.sendEmail(email, 'Beslissing op je aanvraag',
'Je aanvraag is ' + status.toLowerCase() + '.');
}
Voorkom dubbele verwerking bij gelijktijdige klikken
Twee snelle klikken kunnen elkaar overlappen voordat de status is weggeschreven. Gebruik LockService.getScriptLock() rond het lezen en schrijven van de rij, zodat slechts een klik per aanvraag de status mag wijzigen. Zo blijft de statuscheck betrouwbaar, ook bij gelijktijdige aanvragen.
De goedkeuringsflow opzetten
- Maak een Google Form met e-mailverzameling aan en koppel een spreadsheet via Antwoorden.
- Schrijf de
bijAanvraag-functie die logt en de goedkeurder mailt, en koppel een installeerbare onFormSubmit-trigger. - Bouw de
doGet-webapp die acties valideert en de status bijwerkt. - Klik op Implementeren, kies Nieuwe implementatie en daarna type Web-app, en zet de URL in je mailfunctie.
- Test de hele keten met een proefaanvraag van begin tot eind.
Hoe voorkom ik dat iemand zonder rechten goedkeurt?
Zet de webapp op toegang binnen je organisatie en laat hem uitvoeren als de toegankende gebruiker. Controleer in doGet de waarde van Session.getActiveUser().getEmail() tegen een lijst van toegestane goedkeurders en weiger elke andere klik.
Wat als de goedkeurder twee keer klikt?
De statuscheck In behandeling vangt dit op: bij de tweede klik is de status al gewijzigd en toont de webapp dat de aanvraag al verwerkt is. Wikkel het lezen en schrijven in een LockService-slot om ook gelijktijdige klikken af te vangen.
Kan ik meerdere goedkeurders in serie hebben?
Ja. Voeg een statuskolom per niveau toe en stuur na de eerste goedkeuring een mail naar de volgende goedkeurder voordat je definitief afrondt.
Hoe maak ik de beslislinks veiliger?
Voeg naast de id een ondertekend token toe dat je in de spreadsheet bewaart en in doGet vergelijkt, zodat een geraden id alleen niet volstaat. Een token genereer je bijvoorbeeld met Utilities.computeHmacSha256Signature over de id plus een geheim.
Waarom zie ik geen mail nadat ik het formulier invul?
Vrijwel altijd ontbreekt de installeerbare onFormSubmit-trigger of zijn de machtigingen nog niet geaccepteerd. Voer de functie eerst een keer handmatig uit zodat je de toegang goedkeurt, en controleer daarna in de triggerlijst of de trigger echt is gekoppeld.
Hoeveel mails mag ik per dag versturen?
Apps Script kent een dagelijkse maillimiet die afhangt van je accounttype, lager voor gratis accounts en hoger voor Workspace-abonnementen. Lees de actuele limiet uit met MailApp.getRemainingDailyQuota() en bouw een terugvalpad als die op nul staat.
Volgende stap
Deze flow bouwt voort op formulierreacties verwerken en webapps. Beveilig hem verder met de adviezen uit best practices.