Impression : démarche et technique

Bonjour,

Je poursuis ma tentative d'organiser le retour des manuels pour un collège.

Le formulaire que j'ai créé (gros coup de main de Sébastien) me permet d'afficher les infos permettant le contrôle et la réception. Je voudrais maintenant imprimer un document de réception pour l'élève (avec une petite mise en forme , feuille "courrier_perdu"). Voir le document ici : https://docs.google.com/spreadsheets/d/1vYgpDSxzF4K-YuStMX44LurL4kAeU3rmVM65JVeD6WI/edit?usp=sharing

J'ai créé une feuille qui contient le modèle à éditer. Dans l'idéal il faudrait donc :

- que les infos utiles (nom, classe, titre, prix...) soient copiées dans la feuille "courrier_perdu" en cliquant sur le bouton imprimer (D21 - "Formulaire controle")

- que l'impression se lance automatiquement après remplissage des champs dans "courrier_perdu"

- qu'à l'issue de l'impression le formulaire "courrier_perdu" soit remis à zéro pour un nouvel usage

Mais peut être y-a-t-il plus simple, je ne suis pas sûr que ma logique soit la bonne et je n'ai pas trouvé de modèle de script s'approchant de mon besoin.

Peut-être faut-il un script à part pour la remise à zéro ?

Les bonnes idées sont les bienvenues,

Merci

Bonjour,

J'ai récemment fait un script similaire, à partir d'un BDD, je génère des certificats autocomplétés.

0 - déclarer les constantes :

const sheet = SpreadsheetApp.getActiveSpreadsheet();
const dataSheet = sheet.getSheetByName('formulaire_controle');
const template = sheet.getSheetByName('courrier_perdu');

1- nettoyer le modèle : Il est aussi possible de mettre ce code à la fin pour nettoyer une fois l'impression effectuée.

  const nettoyageTemplate = template.getRangeList(['B4','B14:C46','C48']).getRanges(); // les plages sont à adapter
  nettoyageTemplate.forEach(function (cell){cell.clearContent();})

2- stocker vos données dans des variables

var nomPrenom = dataSheet.getRange(5,2).getValue();
var classe = dataSheet.getRange(6,2).getValue();

3- insérer les données dans le modèle

var date = template.getRange(1,2).setValue(Utilities.formatDate(new Date(),"GMT+2","dd/MM/yyyy"));
var courier_nom = template.getRange(3,2).setValue(nomPrenom);
var courier_classe = template.getRange(3,5).setValue(classe);

> Je vous conseille de déjà adapter cela et verifier si ça fonctionne, ensuite passer à l'impression automatisée.

4- Pour l'impression, c'est un peu plus compliqué, voici un code à adapter (suivre les annotations) :

var optionImpression = {
  'size': 7,               // choix du format papier. 0=letter, 1=tabloid, 2=Legal, 3=statement, 4=executive, 5=folio, 6=A3, 7=A4, 8=A5, 9=B4, 10=B
  'fzr': false,            // affiche les entetes
  'portrait': true,        // false=paysage
  'fitw': true,            // la taille s'adapte à la fenetre
  'gridlines': false,      // affiche les grille
  'printtitle': false,     // imprime le titre 
  'sheetnames': false,     // imprime le nom de la feuille 
  'pagenum': 'UNDEFINED',  // CENTER = affiche les numéros de page / UNDEFINED = ne les affiches pas
  'attachment': false
}

var PDF_OPTS = objectToQueryString(optionImpression);

function impressionPlage() {
  SpreadsheetApp.flush();
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getActiveSheet();
  var range = sheet.getRange("A4:H35"); // plage d'impression à adapter

  var gid = sheet.getSheetId();
  var printRange = objectToQueryString({
    'c1': range.getColumn() - 1,
    'r1': range.getRow() - 1,
    'c2': range.getColumn() + range.getWidth() - 1,
    'r2': range.getRow() + range.getHeight() - 1
  });
  var url = ss.getUrl().replace(/edit$/, '') + 'export?format=pdf' + PDF_OPTS + printRange + "&gid=" + gid;
  var htmlTemplate = HtmlService.createTemplateFromFile('js');
  htmlTemplate.url = url;
  SpreadsheetApp.getUi().showModalDialog(htmlTemplate.evaluate().setHeight(10).setWidth(100), "Ouverture de l'impression");
}

function objectToQueryString(obj) {
  return Object.keys(obj).map(function(key) {
    return Utilities.formatString('&%s=%s', key, obj[key]);
  }).join('');
}

+ il faut un code en HTML (à créer dans google app script en HTML et il faut le nommer js :

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <script>
  window.open('<?=url?>', '_blank', 'width=800, height=600');
  google.script.host.close();
</script>
  </body>
</html>

Bonjour,

Merci de votre réponse. J'ai tenté d'adapter votre code. Un bouton impression,depuis le formulaire, lance le script.

C'est ok pour l'ajout des infos depuis le formulaire vers la feuille "courrier_perdu", c'est déjà très bien, par contre rien ne se passe côté impression. Pas de message d'erreur non plus.

Je cale donc sur l'impression. Une question en passant : la logique est d'afficher un pdf intermédiaire qui permet l'impression ou faire l'impression directe ?

(j'ai aussi créé le fichier html nommé "js" avec votre code)

Voici le script adapté d'après votre code :

function impression() {
//déclare les constantes
const sheet = SpreadsheetApp.getActiveSpreadsheet();
const dataSheet = sheet.getSheetByName('formulaire_controle');
const template = sheet.getSheetByName('courrier_perdu');
//stocke donnees dans variables
var nomPrenom = dataSheet.getRange(5,2).getValue();
var classe = dataSheet.getRange(6,2).getValue();
var isbn = dataSheet.getRange(18,2).getValue();
var titre = dataSheet.getRange(19,2).getValue();
var discipline = dataSheet.getRange(19,6).getValue();
var niveau = dataSheet.getRange(19,4).getValue();
//inserer donnees dans modele
var date = template.getRange(1,2).setValue(Utilities.formatDate(new Date(),"GMT+2","dd/MM/yyyy"));
var courier_nomPrenom = template.getRange(3,2).setValue(nomPrenom);
var courier_classe = template.getRange(3,5).setValue(classe);
var courier_isbn = template.getRange(9,1).setValue(isbn);
var courier_titte = template.getRange(9,2).setValue(titre);
var courier_discipline = template.getRange(9,3).setValue(discipline);
var courier_niveau = template.getRange(9,4).setValue(niveau);
// nettoyage
//const nettoyageTemplate = template.getRangeList(['B3','C3','B9','C9','D9','E9',]).getRanges(); // les plages sont à adapter
//nettoyageTemplate.forEach(function (cell){cell.clearContent();})

var optionImpression = {
  'size': 7,               // choix du format papier. 0=letter, 1=tabloid, 2=Legal, 3=statement, 4=executive, 5=folio, 6=A3, 7=A4, 8=A5, 9=B4, 10=B
  'fzr': false,            // affiche les entetes
  'portrait': true,        // false=paysage
  'fitw': true,            // la taille s'adapte à la fenetre
  'gridlines': false,      // affiche les grille
  'printtitle': false,     // imprime le titre 
  'sheetnames': false,     // imprime le nom de la feuille 
  'pagenum': 'UNDEFINED',  // CENTER = affiche les numéros de page / UNDEFINED = ne les affiches pas
  'attachment': false
}

var PDF_OPTS = objectToQueryString(optionImpression);

function impressionPlage() {
  SpreadsheetApp.flush();
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getActiveSheet();
  var range = sheet.getRange("A1:E20"); // plage d'impression à adapter

  var gid = sheet.getSheetId();
  var printRange = objectToQueryString({
    'c1': range.getColumn() - 1,
    'r1': range.getRow() - 1,
    'c2': range.getColumn() + range.getWidth() - 1,
    'r2': range.getRow() + range.getHeight() - 1
  });
  var url = ss.getUrl().replace(/edit$/, '') + 'export?format=pdf' + PDF_OPTS + printRange + "&gid=" + gid;
  var htmlTemplate = HtmlService.createTemplateFromFile('js');
  htmlTemplate.url = url;
  SpreadsheetApp.getUi().showModalDialog(htmlTemplate.evaluate().setHeight(10).setWidth(100), "Ouverture de l'impression");
}

function objectToQueryString(obj) {
  return Object.keys(obj).map(function(key) {
    return Utilities.formatString('&%s=%s', key, obj[key]);
  }).join('');
}
};

Bonjour,

Actuellement, lorsque vous lancez la fonction impression, elle se lance, mais n'appelle pas d'autres fonctions, il faut ajouter, à la fin de votre script impression, la ligne :

screenshot 2023 05 31 10 08 49

afin qu'une fois les informations appliquées au modèle, elle lance la fonction : impressionPlage

PS : concernant les boutons, vous pouvez sur internet trouver des sites qui permettent d'en faire des personnalisés très simplement, ex : https://www.clickminded.com/button-generator/

J'ai rajouté la ligne : impressionPlage()

Lorsque je lance le script, les données sont bien copiées dans "courrier_perdu", j'ai une petite fenêtre pop up qui affiche "ouverture de l'impression", puis plus rien et pas d'impression.

J'ai bien sûr testé l'imprimante, ça fonctionne. J'utilise Firefox et c'est ce navigateur qui est désigné dans les paramètres pour l'affichage des pdf. Je ne vois pas de paramètre bloquant dans firefox, pas de message de sa part non plus.

Ca coince donc ailleurs...

J'avais oublié d'autoriser les pop up pour le formulaire. Désormais j'ai donc : la pop up "ouverture de l'impression" puis une autre pop up affichant un message d'erreur. Voir capture écran :

pop up erreur

J'ai essayé de mon côté, c'est un souci d'imbrication de fonction, avec les symboles { }

Voici le code complet et fonctionnel : (l'erreur résidait à la fin de votre code, fonctions n'étaient pas séparées, mais intégrées entre elles, j'ai juste remis à leurs places les accolades).

function enregistrer() {
  const feuille1 = SpreadsheetApp.getActiveSheet();
  const feuille2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('tableau_recapitulatif');
  feuille2.appendRow([
    feuille1.getRange('B6').getValue(),
    feuille1.getRange('B5').getValue(),
    feuille1.getRange('D13').getValue(),    
  ]);
  SpreadsheetApp.getActiveSpreadsheet().toast('Vos informations sont bien enregistrées !', 'Confirmation');
}

function impression() {
//déclare les constantes
const sheet = SpreadsheetApp.getActiveSpreadsheet();
const dataSheet = sheet.getSheetByName('formulaire_controle');
const template = sheet.getSheetByName('courrier_perdu');
//stocke donnees dans variables
var nomPrenom = dataSheet.getRange(5,2).getValue();
var classe = dataSheet.getRange(6,2).getValue();
var isbn = dataSheet.getRange(18,2).getValue();
var titre = dataSheet.getRange(19,2).getValue();
var discipline = dataSheet.getRange(19,6).getValue();
var niveau = dataSheet.getRange(19,4).getValue();
//inserer donnees dans modele
var date = template.getRange(1,2).setValue(Utilities.formatDate(new Date(),"GMT+2","dd/MM/yyyy"));
var courier_nomPrenom = template.getRange(3,2).setValue(nomPrenom);
var courier_classe = template.getRange(3,5).setValue(classe);
var courier_isbn = template.getRange(9,1).setValue(isbn);
var courier_titte = template.getRange(9,2).setValue(titre);
var courier_discipline = template.getRange(9,3).setValue(discipline);
var courier_niveau = template.getRange(9,4).setValue(niveau);
impressionPlage()
// nettoyage
//const nettoyageTemplate = template.getRangeList(['B3','C3','B9','C9','D9','E9',]).getRanges(); // les plages sont à adapter
//nettoyageTemplate.forEach(function (cell){cell.clearContent();})
}
var optionImpression = {
  'size': 7,               // choix du format papier. 0=letter, 1=tabloid, 2=Legal, 3=statement, 4=executive, 5=folio, 6=A3, 7=A4, 8=A5, 9=B4, 10=B
  'fzr': false,            // affiche les entetes
  'portrait': true,        // false=paysage
  'fitw': true,            // la taille s'adapte à la fenetre
  'gridlines': false,      // affiche les grille
  'printtitle': false,     // imprime le titre 
  'sheetnames': false,     // imprime le nom de la feuille 
  'pagenum': 'UNDEFINED',  // CENTER = affiche les numéros de page / UNDEFINED = ne les affiches pas
  'attachment': false
}

var PDF_OPTS = objectToQueryString(optionImpression);

function impressionPlage() {
  SpreadsheetApp.flush();
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheetByName('courrier_perdu');
  var range = sheet.getRange("A1:E20"); // plage d'impression à adapter

  var gid = sheet.getSheetId();
  var printRange = objectToQueryString({
    'c1': range.getColumn() - 1,
    'r1': range.getRow() - 1,
    'c2': range.getColumn() + range.getWidth() - 1,
    'r2': range.getRow() + range.getHeight() - 1
  });
  var url = ss.getUrl().replace(/edit$/, '') + 'export?format=pdf' + PDF_OPTS + printRange + "&gid=" + gid;
  var htmlTemplate = HtmlService.createTemplateFromFile('js');
  Logger.log(url)

  htmlTemplate.url = url;
  SpreadsheetApp.getUi().showModalDialog(htmlTemplate.evaluate().setHeight(10).setWidth(100), "Ouverture de l'impression");
}

function objectToQueryString(obj) {
  return Object.keys(obj).map(function(key) {
    return Utilities.formatString('&%s=%s', key, obj[key]);
  }).join('');
  }

Excellent, l'impression fonctionne parfaitement : un grand merci, vous me rendez un fier service.

Dans la foulée j'ai tenté de partager le fichier sheets avec un collègue, l'idée est que deux opérateurs (2 pc) renseignent ce document lors de l'opération du retour des livres. On pourrait ainsi faire 2 files d'élèves et gagner du temps.

Même en lui donnant les droits d'éditeurs lors du partage, il est coincé par un message disant que l'exécution des macros peut être dangereux.

Vous sauriez s'il y a un moyen de contourner cela ? (voir capture ecran) :

autorisation google

Par ailleurs j'ai bien noté votre remarque pertinente sur l'esthétique de mon formulaire. C'est un prototype qui me permet de valider ma logique et la faisabilité du raisonnement, je compte bien améliorer la présentation. Merci pour le lien vers l'outil de création de boutons.

J'imagine que c'est votre e-mail qui apparait.

Personnellement mes fichiers et scripts sont diffusés dans mon organisation et nous sommes en contrat pro avec Google donc niveau sécurité, je n'ai pas de problématique.

Je sais que Google contraint l'accès aux périphériques et à des actions pouvant être utilisées afin de hacker, c'est pour cela que le code utilisé pour imprimer est si complexe (car il n'est pas prévu de pouvoir lancer une impression, qui nécessite d'accéder aux périphériques), par rapport au reste du code, c'est sans doute cela qui est détecté comme une menace.

Si votre collège clique sur "Paramètres avancés" et qu'il force le lancement, est-ce que ça fonctionne normalement ou bien ça lui fait à chaque fois ?

> Si ça ne lui fait qu'une fois au 1er lancement, c'est à savoir mais pas handicapant.

> Si c'est systématique, je vous propose de passer par l'enregistrement automatique d'un PDF, ou bien à imprimer manuelement ensuite, ou bien envoyé par mail.

Bonjour,

Même en lui donnant les droits d'éditeurs lors du partage, il est coincé par un message disant que l'exécution des macros peut être dangereux.

C'est un message pas très rassurant mais tout à fait normal (il apparaît même avec une simple fonction qui écrit bonjour en A1)

https://www.sheets-pratique.com/fr/codes/autorisation

Personnellement, j'ai cette demande d'autorisation (presque) à chaque nouveau fichier qui contient des macros (mais c'est apparemment différent pour les comptes pro comme le souligne Pierre).

Cordialement,

Effectivement, je n'avais pas pensé aux paramètres avancés. Nouvel essai + paramètres avancés + accepter malgré le risque = ça fonctionne chez le collègue.

C'est un peu déroutant mais pas rédhibitoire. Je considère donc que c'est ok. Grand merci à Pierre. Merci Sébastien de confirmer que la situation est assez commune.

L'idée de l'envoi d'un mail, simultanément à l'impression, ne m'avait pas effleuré. Je trouve ça très séduisant si ça ne débouche pas sur une usine à gaz. J'imagine qu'il faut un serveur smtp (smtp.gmail.com ?), les codes et un bout de script supplémentaire ? D'autres contraintes ?

L'envoi de mails à l'aide de Google apps script est plutôt simple :

1- Stocker les adresses mails dans un variable, ou bien directement dans le script, il est aussi possible de faire référence à des cellules (cela permet de changer facilement)

  var email = 'ceciestuneadressemail@gmail.com, autremail@gmail.com';

2- Préparer le corps du message, il est possible d'y ajouter des données précédemment stockées dans des variables :

  var message = "Bonjour"+"<p> Le courrier concernant le livre "+ titre+"<p> ISBN : "+isbn+" <p> est imprimé";

3- envoyer le mail :

    MailApp.sendEmail({to: email , subject: "ceci est l'objet du mail", htmlBody:message}) ;}

+ il est aussi possible de joindre le PDF en pièce jointe, pour cela, il faut enregistrer le PDF sur votre drive :

  // enregistrement du PDF dans le Drive
  var folder = DriveApp.getFolderById("17LmzDRdzPpH3zt_4rufGLnGXbBWbQNo5");/* Important ! > Ici on doit mettre l'adresse du dossier drive où on veut sauvegarder le PDF, allez dans votre dossier et dans la barre d'URL en haut copier le code qui ressemble à ça : 0AGuVhN09TP00Uk9PVA */
  var finalFile = file.moveTo(folder);
  var docUrl = finalFile.getUrl();

Puis dans le message il faut ajouter le docUrl :

  var message = "Bonjour"+"<p> Le courrier concernant le livre "+ titre+"<p> ISBN : "+isbn+" <p> est imprimé<a href='"+docUrl+"'>Cliquez ici pour ouvrir le courrier.</a>;
"Il est aussi possible de faire référence à des cellules"

D'accord mais il faut aussi lier le nom de l'élève à ce mail non ?
Je peux ajouter dans la feuille "liste_eleves", le mail du représentant légal.

Si je récapitule, il faudrait donc que le bouton impression déclenche :

- la copie des infos dans le feuille courrier_perdu -> c'est ok

- l'impression du courrier -> c'est ok

- la récupération du mail du représentant légal correspondant à l'élève dans la feuille "liste_eleve" -> ????

- l'envoi du mail -> code à tester

Il y a peut-être plus simple mais essayez ceci :

  var emails = mailSheet.getRange(2,1,mailSheet.getLastRow(),6).getValues();
  var email = [];
  for(i=0;i<emails.length;i++){
    if(emails[i][0]==nomPrenom){
      email.push(emails[i][5])}}
  Logger.log(email)

Bonjour,

Je reviens sur ma réponse d'hier, le plus simple (plutôt que de créer une boucle avec condition) c'est dans votre formulaire contrôle, faire apparaitre l'email du parent à l'aide d'une simple RECHERCHEX et de stocker ce mail dans un variable, comme vous l'avez fait pour les autres données.

Bonjour,

Pour la partie mail, je tourne un peu en rond, message d'erreur : copie impossible.

Voici le morceau de code ajouté au script "impression" :

//mail
 function sendEmails() {
  //stockage mail dans variable
  var mail = dataSheet.getRange(7,2).getValue();
    var courier_mail = template.getRange(4,2).setValue(mail);
  //Préparer le corps du message, il est possible d'y ajouter des données précédemment stockées dans des variables 
    var message = "Bonjour"+"<p> Le courrier concernant le livre "+ titre+"<p> ISBN : "+isbn+" <p> est imprimé";
  // enregistrement du PDF dans le Drive
  var folder = DriveApp.getFolderById("1wWxp.....qggYbtSYptZfBfRSEsmyTb9");/* Important ! > Ici on doit mettre l'adresse du dossier drive où on veut sauvegarder le PDF, allez dans votre dossier et dans la barre d'URL en haut copier le code qui ressemble à ça : 0AGuVhN09TP00Uk9PVA */
  var finalFile = file.moveTo(folder);
  var docUrl = finalFile.getUrl();
  //Puis dans le message il faut ajouter le docUrl
var message = "Bonjour"+"<p> Le courrier concernant le livre "+ titre+"<p> ISBN : "+isbn+" <p> est imprimé<a href='"+docUrl+"'>Cliquez ici pour ouvrir le courrier.</a>;
  //envoi du mail
    MailApp.sendEmail({to: mail , subject: "Manuel scolaire non rendu", htmlBody:message}) ;}

J'y retournerai à tête reposée demain.

Pour l'envoi du mail j'ai finalement laissé tomber. L'idée est séduisante mais je ne maitrise pas suffisamment les scripts pour en venir à bout. Je manque par ailleurs de temps et de disponibilité.

Toutes les solutions de PierreLepinay pour l'impression sont fonctionnelles et ça m'avance déjà beaucoup.

Je considère donc mon sujet résolu.

Rechercher des sujets similaires à "impression demarche technique"