{"id":5067,"date":"2022-12-09T09:57:52","date_gmt":"2022-12-09T08:57:52","guid":{"rendered":"https:\/\/gpmfactory.com\/?p=5067"},"modified":"2022-12-09T09:57:52","modified_gmt":"2022-12-09T08:57:52","slug":"une-application-de-sav-avec-oracle-apex-et-google-forms","status":"publish","type":"post","link":"https:\/\/gpmfactory.com\/index.php\/2022\/12\/09\/une-application-de-sav-avec-oracle-apex-et-google-forms\/","title":{"rendered":"Une application de SAV avec Oracle APEX et Google Forms"},"content":{"rendered":"\n\n\n<h2 class=\"wp-block-heading\">Objet<\/h2>\n\n\n\n<p>C&rsquo;est l&rsquo;histoire d&rsquo;une application de SAV (service apr\u00e8s vente) r\u00e9alis\u00e9e initialement avec Oracle APEX et compl\u00e9t\u00e9e ult\u00e9rieurement avec des module de Google Workspace. Cela a permis de constituer deux sous-syst\u00e8mes \u00e9tanches, l&rsquo;un destin\u00e9 au backoffice pour les techniciens charg\u00e9s du support et l&rsquo;autre destin\u00e9 aux interactions avec les clients du SAV.<\/p>\n\n\n\n<p>Oracle APEX est utilis\u00e9 en version<a href=\"https:\/\/www.oracle.com\/fr\/cloud\/free\/\"> <em>free tier<\/em><\/a> (avec des limitations mais &#8230; gratuit !) dans la mesure o\u00f9 il y a un petit nombre  d&rsquo;analystes (inf\u00e9rieur \u00e0 cinq dans le contexte de mon projet) et donc de faibles contraintes de concurrence d&rsquo;acc\u00e8s et de charge CPU. Toutes les interactions avec les clients sont g\u00e9r\u00e9es avec des formulaires <a href=\"https:\/\/www.google.fr\/intl\/fr\/forms\/about\/\">Google Forms<\/a>, des <a href=\"https:\/\/developers.google.com\/apps-script\">scripts<\/a>, des envois\/r\u00e9ceptions de mail et du stockage de pi\u00e8ces attach\u00e9es sur Google Drive. <\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Sch\u00e9ma g\u00e9n\u00e9ral d&rsquo;architecture:<\/h2>\n\n\n\n<p><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"519\" src=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/archi-SAV-Generique-1-1024x519.png\" alt=\"\" class=\"wp-image-5093\" srcset=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/archi-SAV-Generique-1-1024x519.png 1024w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/archi-SAV-Generique-1-300x152.png 300w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/archi-SAV-Generique-1-768x389.png 768w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/archi-SAV-Generique-1-1536x778.png 1536w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/archi-SAV-Generique-1.png 1836w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Sous-syst\u00e8mes<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Sous-syst\u00e8me de backoffice<\/h3>\n\n\n\n<p>Je suis parti d&rsquo;une application standard qui \u00e9tait fournie avec les pr\u00e9c\u00e9dentes versions d&rsquo;Oracle APEX et qui s&rsquo;appelait <em><a href=\"https:\/\/www.youtube.com\/watch?v=UHR0DTMF3RE\">Incident Tracking<\/a><\/em>.  Puisque cette application n&rsquo;est plus disponible dans les version r\u00e9centes d&rsquo;APEX, je l&rsquo;ai r\u00e9cup\u00e9r\u00e9e depuis une version 18.x. Cela m&rsquo;a permis de r\u00e9utiliser le mod\u00e8le de donn\u00e9es qui \u00e9tait de qualit\u00e9 et de proposer un d\u00e9marrage rapide. Mais au fur et \u00e0 mesure, il s&rsquo;est av\u00e9r\u00e9 que l&rsquo;\u00e9cart fonctionnel \u00e9tait plus grand que pr\u00e9vu. R\u00e9trospectivement, je serais plut\u00f4t parti d&rsquo;une feuille blanche, tout en conservant le mod\u00e8le de donn\u00e9es.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"492\" src=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-6-1024x492.png\" alt=\"\" class=\"wp-image-5108\" srcset=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-6-1024x492.png 1024w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-6-300x144.png 300w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-6-768x369.png 768w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-6-1536x738.png 1536w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-6.png 1578w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Liste des tickets ouverts<\/figcaption><\/figure>\n\n\n\n<p>L&rsquo;allure g\u00e9n\u00e9rale est tr\u00e8s proche de celle d&rsquo;origine, mais il y a eu beaucoup de r\u00e8gles m\u00e9tiers sp\u00e9cifiques qui ont \u00e9t\u00e9 rajout\u00e9es et qui ont fait bouger \u00e9galement le mod\u00e8le de donn\u00e9es. Toutes les informations du clients sont r\u00e9cup\u00e9r\u00e9s, en particulier, depuis un ERP par appel d&rsquo;un service Web. Pour le reste, il s&rsquo;agit d&rsquo;une application classique de gestion.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"523\" src=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-10-1024x523.png\" alt=\"\" class=\"wp-image-5117\" srcset=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-10-1024x523.png 1024w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-10-300x153.png 300w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-10-768x393.png 768w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-10-1536x785.png 1536w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-10.png 1581w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">R\u00e9sum\u00e9 d&rsquo;un ticket<\/figcaption><\/figure>\n\n\n\n<p>Les \u00e9l\u00e9ments de conversation entre le(s) technicien(s) et le client sont rassembl\u00e9s dans un onglet <em>Conversations<\/em> et sont issus du sous-syst\u00e8me d&rsquo;interactions, tel que d\u00e9crit plus loin.<\/p>\n\n\n\n<p>L&rsquo;onglet <em>Documents <\/em>contient la liste des liens vers <em>Google Drive<\/em> dans le cas o\u00f9 le client aurait envoy\u00e9 des fichiers descriptif du probl\u00e8me sous forme de photos et vid\u00e9os.<\/p>\n\n\n\n<p>J&rsquo;ai profit\u00e9 des nouveaut\u00e9s des version r\u00e9centes d&rsquo;APEX, en particulier la recherche par facettes, pour am\u00e9liorer l&rsquo;ergonomie et j&rsquo;ai rajout\u00e9 la possibilit\u00e9 de se connecter avec son compte Google Workspace puisque chaque technicien en disposait.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Fonctionnement sur mobiles<\/h4>\n\n\n\n<p>Tr\u00e8s peu de modifications ont \u00e9t\u00e9 n\u00e9cessaires pour obtenir un fonctionnement correct sur mobile. J&rsquo;ai simplement utilis\u00e9 des classes (hidden-xxs-down et hidden-xs-up) pour switcher sur une pr\u00e9sentation plut\u00f4t qu&rsquo;une autre au sein d&rsquo;une m\u00eame page. C&rsquo;est typiquement le cas pour les listes de type \u00ab\u00a0Interactive Grid\u00a0\u00bb qui auraient impos\u00e9 du scrolling sur un petit \u00e9cran et auxquelles j&rsquo;ai substitu\u00e9 des <em>list View<\/em>s.<br>Les autres composants Oracle APEX s&rsquo;adaptent automatiquement \u00e0 la g\u00e9om\u00e9trie du <em>device<\/em>.<br>J&rsquo;en ai profit\u00e9 pour mettre un peu de <a href=\"https:\/\/docs.oracle.com\/en\/database\/oracle\/application-express\/21.2\/htmdb\/crreating-a-progressive-web-application.html\">PWA <\/a>pour gagner un peu d&rsquo;espace sur l&rsquo;\u00e9cran du mobile et proposer une icone plus naturelle qu&rsquo;un raccourci du Navigateur.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Sous-syst\u00e8me de FrontOffice <\/h3>\n\n\n\n<p>Ce deuxi\u00e8me bloc est destin\u00e9 aux interactions avec les clients finaux. Il repose sur l&#8217;emploi de plusieurs modules de la suite <em><a href=\"https:\/\/workspace.google.fr\/intl\/fr\/\">Google Workspace<\/a><\/em>. La compagnie disposait d\u00e9j\u00e0 d&rsquo;un abonnement \u00e0 ce service pour la messagerie et il n&rsquo;a pas \u00e9t\u00e9 n\u00e9cessaire de souscrire \u00e0 des options compl\u00e9mentaires.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Formulaires d&rsquo;interaction<\/h4>\n\n\n\n<p>Concevoir et mettre \u00e0 disposition des formulaires d&rsquo;ouverture de ticket ou de demande de statut directement sous Oracle APEX aurait entrain\u00e9 la communication de l&rsquo;url du <em>backoffice <\/em>aupr\u00e8s de chaque client. Un contournement \u00e9ventuel aurait consist\u00e9 \u00e0 <a href=\"https:\/\/blogs.oracle.com\/apex\/post\/introducing-vanity-urls-on-adb\">utiliser le <em>Load Balancer<\/em> disponible dans le <em>tenant <\/em>en reverse Proxy<\/a> et d&rsquo;y placer des r\u00e8gle de routage en fonction de l&rsquo;application \u00e0 utiliser. A l&rsquo;\u00e9poque, je n&rsquo;\u00e9tais pas familier avec la technique pour le faire et, de toute mani\u00e8re, cela aurait pos\u00e9 le probl\u00e8me de la charge du syst\u00e8me car si je connaissais le nombre d&rsquo;utilisateurs internes (les analystes), je ne connaissais pas le nombre de clients qui se connectent \u00e0 un instant donn\u00e9. Un nombre \u00e9lev\u00e9 de connexions aurait entrain\u00e9  la fermeture arbitraire de sessions d\u00e9j\u00e0 ouvertes, puisqu&rsquo;il y a un <a href=\"https:\/\/docs.oracle.com\/en\/cloud\/paas\/apex\/gsadd\/always-free-oracle-apex-application-development.html#GUID-0B0617FC-1E69-482A-9EFF-6E2D3F7EA0B5\">pool limit\u00e9 dans le cas de Oracle Free Tiers<\/a>, et cela aurait p\u00e9nalis\u00e9 l&rsquo;acc\u00e8s par les technicien comme le laisse supposer l&rsquo;extrait suivant des <a href=\"https:\/\/docs.oracle.com\/en\/cloud\/paas\/apex\/gsadd\/always-free-oracle-apex-application-development.html#GUID-0B0617FC-1E69-482A-9EFF-6E2D3F7EA0B5\">limitations<\/a>:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>The HTTP interface of&nbsp;Always Free APEX Service&nbsp;is rate limited to restrict the number of simultaneous service users. Approximately 3-6 simultaneous users can be supported across all of APEX, Oracle REST Data Services, and SQL Developer Web<\/p>\n<\/blockquote>\n\n\n\n<p>J&rsquo;ai donc fait le choix d&rsquo;un sous-syst\u00e8me compl\u00e9tement s\u00e9par\u00e9, responsable uniquement des interactions avec les clients. Google Forms \u00e9tait un choix disons &#8230; spontan\u00e9, puisqu&rsquo;il \u00e9tait question de formulaires.<\/p>\n\n\n\n<p>Tous les formulaires ont \u00e9t\u00e9 con\u00e7us avec Google Forms. Autant le pr\u00e9ciser de suite, ce service souffre de beaucoup de limitations car il est impossible, par exemple, de proposer une liste de valeurs dynamique. Il n&rsquo;est pas possible non plus d&rsquo;afficher un champs en mode <em>read-only<\/em>. Il a fallu donc s&rsquo;accommoder de nombreuses limitations. C&rsquo;\u00e9tait le prix \u00e0 payer pour avoir l&rsquo;esprit tranquille quant aux consid\u00e9rations de s\u00e9curit\u00e9 et de mont\u00e9e en charge. Il existe bien s\u00fbr des plugins sp\u00e9cialis\u00e9s pour Google Forms mais ils sont payants ou bien apportent une publicit\u00e9 intempestive.<\/p>\n\n\n\n<h5 class=\"wp-block-heading\">Avantages d&rsquo;utiliser des formulaires externes<\/h5>\n\n\n\n<p>Le client n&rsquo; a jamais connaissance de l&rsquo;existence de l&rsquo;url de <em>backoffice<\/em>. Cela \u00e9vite une publicit\u00e9 non d\u00e9sir\u00e9e du syst\u00e8me. Tout ce qu&rsquo;il voit, c&rsquo;est l&rsquo;interface Google Forms qui, je le suppose, est suffisamment prot\u00e9g\u00e9e contre des attaques de type DOS (<em>Denial of service<\/em>).<\/p>\n\n\n\n<p>Un autre avantage indirecte est que toutes les donn\u00e9es saisies sont enregistr\u00e9es dans une base interne \u00e0 Google et peuvent \u00eatre \u00e9galement copi\u00e9es automatiquement dans un tableau (sheet). Cela constitue un backup des informations entrantes dans le cas ou le SAV viendrait \u00e0 \u00eatre indisponible.<\/p>\n\n\n\n<h5 class=\"wp-block-heading\">Design des formulaires<\/h5>\n\n\n\n<p>Comme la compagnie op\u00e8re avec une marque blanche et des trois distributeurs, il fallait que le bandeau affiche une marques parmi les trois.   <br>Dans chaque formulaire, il a fallu transporter le contexte sous la forme d&rsquo;un champs de type texte.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-2.png\" alt=\"\" class=\"wp-image-5097\" width=\"561\" height=\"667\" srcset=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-2.png 748w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-2-252x300.png 252w\" sizes=\"auto, (max-width: 561px) 100vw, 561px\" \/><figcaption class=\"wp-element-caption\">Formulaire de cr\u00e9ation de ticket aupr\u00e8s du Support<\/figcaption><\/figure>\n\n\n\n<h5 class=\"wp-block-heading\">Formulaires<\/h5>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ouverture de ticket (trois variantes diff\u00e9rentes selon le bandeau)<\/li>\n\n\n\n<li>R\u00e9ouverture de ticket<\/li>\n\n\n\n<li>Demande de statut<\/li>\n\n\n\n<li>Demande de fermeture<\/li>\n\n\n\n<li>Demande de r\u00e9ouverture<\/li>\n\n\n\n<li>R\u00e9ponse \u00e0 une question du SAV<\/li>\n<\/ul>\n\n\n\n<p>Chaque formulaire est accompagn\u00e9 d&rsquo;un script \u00e9crit en langage <a href=\"https:\/\/developers.google.com\/apps-script\">Google Apps Script<\/a>, qui effectue la collecte des champs remplis et qui effectue un appel de service REST synchrone aupr\u00e8s du SAV via <a href=\"https:\/\/www.oracle.com\/fr\/database\/technologies\/appdev\/rest.html\">ORDS <\/a>(cf <a href=\"#Exemple_de_script_accompagnant_un_formulaire_Google\" data-type=\"internal\" data-id=\"#Exemple_de_script_accompagnant_un_formulaire_Google\">exemple en annexe<\/a>). Ce m\u00eame script notifie le client par mail du r\u00e9sultat de l&rsquo;appel. <br>Il y a trois versions de formulaire et le script a \u00e9t\u00e9 mutualis\u00e9 dans une librairie.<\/p>\n\n\n\n<p>Comme il n&rsquo;y a pas d&rsquo;authentification, les formulaires de demande de statut, par exemple, utilisent un contexte qui doit \u00eatre transport\u00e9 par le formulaire. Ce contexte est une chaine de caract\u00e8re qui contient le code du ticket et une valeur de contr\u00f4le. <\/p>\n\n\n\n<h6 class=\"wp-block-heading\">Contexte<\/h6>\n\n\n\n<p>Il est compos\u00e9 du code du ticket sur cinq caract\u00e8res et d&rsquo;un code de contr\u00f4le (hash code \u00e0 partir d&rsquo;informations vari\u00e9es). Il fallait proposer une valeur simple \u00e0 retenir pour le client afin de faciliter sa communication avec le SAV, mais aussi d\u00e9courager toute tentation de tester d&rsquo;autres valeurs de tickets.  <\/p>\n\n\n\n<p>Comme le champs ne peut pas \u00eatre en <em>Read Only<\/em>, rien n&#8217;emp\u00eache l&rsquo;utilisateur de modifier le contenu de ce champs. Le risque se limite au fait que le script qui se d\u00e9clenche apr\u00e8s la soumission du formulaire renverra un contexte que le service REST du SAV ne saura pas retrouver. <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Conversations et Notifications<\/h4>\n\n\n\n<p>Les conversations et notifications s&rsquo;appuient sur l&rsquo;\u00e9change de mails. Il est envisageable d&rsquo;avoir recours a des message SMS. J&rsquo;ai test\u00e9 le service OVH mais cela n&rsquo;a pas \u00e9t\u00e9 mis en production sur ce projet. <\/p>\n\n\n\n<p>Les mails sont g\u00e9n\u00e9r\u00e9s automatiquement par les scripts attach\u00e9s aux formulaires Google \u00e0 l\u2019exception du cas o\u00f9 c\u2019est un technicien qui d\u00e9cide d\u2019envoyer une question. <\/p>\n\n\n\n<p>L&rsquo;analyste n&rsquo;utilise donc que l&rsquo;interface de backoffice pour effectuer son travail de tous les jours. Lorsqu&rsquo;il doit demander des informations compl\u00e9mentaires pour instruire le traitement d&rsquo;un ticket, il passe par une page standard de l&rsquo;application SAV. La soumission de la page d\u00e9clenche alors l&rsquo;appel \u00e0 un script PHP d&rsquo;envoi de mail. (cf <a href=\"#sch\u00e9ma_general_d_architecture\">sch\u00e9ma d&rsquo;architecture plus haut<\/a>).<\/p>\n\n\n\n<p>Lors du d\u00e9but du projet, la version <em>Oracle Free Tier<\/em> \u00e0 la diff\u00e9rence de sa version payante ne permettait pas l&rsquo;envoi de mail , c&rsquo;est \u00e0 dire qu&rsquo;il n&rsquo;y avait pas moyen d&rsquo;invoquer une passerelle SMTP. En cons\u00e9quence, le contournement a consist\u00e9 \u00e0 se priver des fonctionnalit\u00e9s natives d&rsquo;Oracle APEX et envoyer des mail via l&rsquo;API REST de Google Gmail. Celle-ci implique une authentification avec signature des \u00e9changes mais je ne suis pas parvenu \u00e0 utiliser les algorithmes de chiffrement sous PL\/SQL pour d\u00e9clencher OAUTH2 et il n\u2019existe pas de package de connexion pour le PL\/SQL propos\u00e9 par Google. Les tentatives d&rsquo;obtention d&rsquo;un token valide (<code>https:\/\/oauth2.googleapis.com\/token<\/code>) ont \u00e9chou\u00e9 avec la fonction de chiffrage <code>dbms_crypto.SIGN_SHA256_RSA<\/code>. et je me suis donc repli\u00e9 sur l&#8217;emploi d&rsquo;une <em>Gateway <\/em>qui effectue l&rsquo;authentification et l&rsquo;envoi <a href=\"https:\/\/cloud.google.com\/appengine\/docs\/legacy\/standard\/php\/mail\/sending-receiving-with-mail-api\">via du code PHP<\/a>. Dans ce cas, Google fournit une librairie PHP pr\u00eate \u00e0 l&#8217;emploi, <em>Google Client Library for PHP<\/em>. J&rsquo;ai utilis\u00e9 une instance <em>compute <\/em>disponible dans le tenant <em>free Tier<\/em> pour h\u00e9berger cette passerelle et je l&rsquo;ai munie d&rsquo;un certificat SSL pour permettre la communication avec l&rsquo;instance APEX.<\/p>\n\n\n\n<p>Actuellement, (d\u00e9cembre 2022), <a href=\"https:\/\/docs.oracle.com\/en-us\/iaas\/Content\/Email\/Concepts\/overview.htm\">ces limitation ont \u00e9t\u00e9 lev\u00e9es en partie <\/a>puisqu&rsquo;on peut d\u00e9sormais envoyer des mails avec certains seuils d&rsquo;usage, mais qui auraient \u00e9t\u00e9 acceptables dans le cadre du projet (cf <a href=\"#Limites_au_service_Email_Delivery_Service\" data-type=\"internal\" data-id=\"#Limites_au_service_Email_Delivery_Service\">annexes<\/a>).<\/p>\n\n\n\n<h5 class=\"wp-block-heading\">Types de mail<\/h5>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Confirmation d&rsquo;ouverture de ticket<\/li>\n\n\n\n<li>Demande de r\u00e9ponse \u00e0 une question du SAV<\/li>\n\n\n\n<li>Fourniture du statut d&rsquo;un ticket<\/li>\n<\/ul>\n\n\n\n<p>Tous les mails contiennent dans leur objet, le contexte (Code Ticket et code de contr\u00f4le) et proposent dans leurs bas de page deux boutons permettant de demander, soit le statut du ticket soit sa fermeture, soit sa r\u00e9ouverture.<\/p>\n\n\n\n<p>Donc, le client final n&rsquo;a jamais l&rsquo;initiative d&rsquo;envoyer un mail au SAV. Il peut en revanche compl\u00e9ter son ticket avec des photos ou vid\u00e9os, par r\u00e9ponse au mail initial de notification d&rsquo;ouverture d&rsquo;un ticket.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"662\" src=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-4-1024x662.png\" alt=\"\" class=\"wp-image-5100\" srcset=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-4-1024x662.png 1024w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-4-300x194.png 300w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-4-768x497.png 768w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-4.png 1220w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Mail r\u00e9capitulant la cr\u00e9ation d&rsquo;un ticket<\/figcaption><\/figure>\n\n\n\n<h5 class=\"wp-block-heading\">Extraction des pi\u00e8ces attach\u00e9es<\/h5>\n\n\n\n<p>R\u00e9guli\u00e8rement, un script Google bas\u00e9 sur un <em>timer <\/em>se d\u00e9clenche toutes les x minutes pour scruter les mails qui seraient munis de pi\u00e8ces attach\u00e9es. Si c&rsquo;est le cas, les fichiers sont recopi\u00e9s dans un dossier de <em>Google Drive<\/em> nomm\u00e9 avec le nom du ticket et un appel REST informe le syst\u00e8me qu&rsquo;un pi\u00e8ce attach\u00e9e est disponible (url de Google Drive). Ce m\u00e9canisme ne peut pas \u00eatre synchrone (cette limitation est document\u00e9e par Google) et c&rsquo;est la raison pour laquelle il faut effectuer du <em>poling<\/em>.<\/p>\n\n\n\n<p>Alternative: Il aurait \u00e9t\u00e9 possible d&rsquo;utiliser un service d&rsquo;int\u00e9gration tel que <a href=\"https:\/\/zapier.com\/\">Zapier<\/a> ou <a href=\"https:\/\/zapier.com\/\">Make<\/a> (Integromat), mais cela aurait induit des couts d&rsquo;usage.<\/p>\n\n\n\n<p>Lorsque le technicien souhaite obtenir un compl\u00e9ment d&rsquo;information, il utilise l&rsquo;application de backoffice pour r\u00e9diger la question et c&rsquo;est le module de SAV qui effectue l&rsquo;envoi de mail. Dans le corps du mail, il y a un texte g\u00e9n\u00e9rique et un bouton qui redirige vers le formulaire de r\u00e9ponse \u00e0 une question du SAV.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"748\" height=\"635\" src=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-8.png\" alt=\"\" class=\"wp-image-5113\" srcset=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-8.png 748w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-8-300x255.png 300w\" sizes=\"auto, (max-width: 748px) 100vw, 748px\" \/><figcaption class=\"wp-element-caption\">Mail demandant des informations compl\u00e9mentaires<\/figcaption><\/figure>\n\n\n\n<p>L&rsquo;appui sur le bouton \u00ab\u00a0<em>Cliquer ici pour r\u00e9pondre<\/em>\u00a0\u00bb redirige vers un formulaire Google tel que celui-ci:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-9.png\" alt=\"\" class=\"wp-image-5115\" width=\"518\" height=\"528\" srcset=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-9.png 691w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/12\/image-9-294x300.png 294w\" sizes=\"auto, (max-width: 518px) 100vw, 518px\" \/><figcaption class=\"wp-element-caption\">Formulaire pour saisir une r\u00e9ponse \u00e0 une question du SAV<\/figcaption><\/figure>\n\n\n\n<p>Lorsque le client veut accompagner sa demande d&rsquo;ouverture de ticket par des photos ou vid\u00e9os, il doit les attacher en r\u00e9ponse au mail de confirmation qu&rsquo;il aura re\u00e7u du SAV. <\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Annexes<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Exemple de script accompagnant un formulaire Google<\/h3>\n\n\n\n<p>Cet extrait de script Google illustre comment sont consomm\u00e9es les informations issues du formulaires, juste apr\u00e8s sa soumission. <br>On voit ici un appel \u00e0 un service REST de cr\u00e9ation de ticket dans SAV et l&rsquo;envoi d&rsquo;un mail de confirmation au client dans le cas o\u00f9 le statut de retour est ok.<\/p>\n\n\n\n<p>Un simple m\u00e9canisme de clef de s\u00e9curit\u00e9 (API_KEY) permet une reconnaissance par le service REST qui est appel\u00e9. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>function&nbsp;createTicket(e,&nbsp;canal)&nbsp;{\n\/\/&nbsp;------------------------------------------\n\/\/&nbsp;Creates&nbsp;a&nbsp;new&nbsp;ticket&nbsp;in&nbsp;SAV&nbsp;subsystem\n\/\/&nbsp;and&nbsp;sends&nbsp;a&nbsp;mail&nbsp;to&nbsp;the&nbsp;customer\n\/\/&nbsp;Gpmfactory 2022\n\/\/&nbsp;------------------------------------------\n...\nvar&nbsp;url&nbsp;=&nbsp;'&lt;SAV REST CREATE TICKET ENDPOINT&gt;';\nvar&nbsp;formData&nbsp;=&nbsp;{\n&nbsp;&nbsp;'EMAIL':&nbsp;email,\n&nbsp;&nbsp;'NAME':&nbsp;t_name,\n&nbsp;&nbsp;'CUSTOMER':&nbsp;t_customer,\n&nbsp;&nbsp;'PHONE':&nbsp;t_phone,\n&nbsp;&nbsp;'DESCRIPTION':&nbsp;t_description,\n&nbsp;&nbsp;...\n&nbsp;&nbsp;'API_KEY':&nbsp;'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'\n};\n\nvar&nbsp;options&nbsp;=&nbsp;{\n&nbsp;&nbsp;'method'&nbsp;:&nbsp;'POST',\n&nbsp;&nbsp;'payload'&nbsp;:&nbsp;formData,\n&nbsp;&nbsp;'muteHttpExceptions':&nbsp;true\n};\n...\nvar&nbsp;response&nbsp;=&nbsp;UrlFetchApp.fetch(url,&nbsp;options);\nvar&nbsp;resp&nbsp;=&nbsp;response.getContentText();\nrc&nbsp;=&nbsp;response.getResponseCode();\nif&nbsp;(rc&nbsp;==&nbsp;\"200\"&nbsp;||&nbsp;rc&nbsp;==&nbsp;\"200.0\"&nbsp;){\n&nbsp;&nbsp;&nbsp;&nbsp;MailApp.sendEmail(email,&nbsp;'Ticket&nbsp;N\u00b0&nbsp;'&nbsp;+&nbsp;resp,&nbsp;body,&nbsp;{'htmlBody'&nbsp;:&nbsp;body&nbsp;});&nbsp;\n&nbsp;&nbsp;&nbsp;&nbsp;Logger.log('Mail&nbsp;envoy\u00e9');&nbsp;\n&nbsp;&nbsp;}\n...<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Limites d&rsquo;Oracle Email Delivery Service pour Free Tier<\/h3>\n\n\n\n<p>A la date de d\u00e9cembre 2022<br><a href=\"https:\/\/docs.oracle.com\/en-us\/iaas\/Content\/Email\/Concepts\/overview.htm\">Source<\/a> <\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Customers that sign up for a free Oracle Cloud trial are limited to:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A volume of 200 emails per day where an email is defined as either a single recipient in the TO:, CC:, or BCC: fields, or a 2 MB chunk of data.Email examples:\n<ul class=\"wp-block-list\">\n<li>A single request with 10 recipients (TO:, CC:, or BCC:) equals 10 emails.<\/li>\n\n\n\n<li>A 10 MB email sent to a single recipient is equal to 10 MB divided by 2 MB per email. This equals 5 emails.<\/li>\n\n\n\n<li>A single email request with a message size of 10 MB sent to 10 recipients is equal to 10 MB divided by 2 MB per email multiplied by 10 recipients. This equals 50 emails.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>2,000 approved senders.<\/li>\n\n\n\n<li>Each user is limited to a maximum of two SMTP credentials.<\/li>\n\n\n\n<li>Sending rates are limited to 10 emails per minute.<\/li>\n\n\n\n<li>Inline attachments and external attachments.<\/li>\n\n\n\n<li>2 MB maximum message size including base64 encoding and headers<\/li>\n<\/ul>\n<\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>Objet C&rsquo;est l&rsquo;histoire d&rsquo;une application de SAV (service apr\u00e8s vente) r\u00e9alis\u00e9e initialement avec Oracle APEX et compl\u00e9t\u00e9e ult\u00e9rieurement avec des module de Google Workspace&#8230;.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[143,142],"ppma_author":[150],"class_list":["post-5067","post","type-post","status-publish","format-standard","hentry","category-non-classe","tag-apres-vente","tag-field-service"],"authors":[{"term_id":150,"user_id":1,"is_guest":0,"slug":"admin8700","display_name":"Patrick","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/209d5ed69b74d288390621ab4c1d3773?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/posts\/5067","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/comments?post=5067"}],"version-history":[{"count":60,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/posts\/5067\/revisions"}],"predecessor-version":[{"id":5140,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/posts\/5067\/revisions\/5140"}],"wp:attachment":[{"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/media?parent=5067"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/categories?post=5067"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/tags?post=5067"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/ppma_author?post=5067"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}