› Sviluppare funzionalita su Microsoft Office con VBA › Salva file con Nome (dati presi da celle diverse) sia in excel che PDF
-
AutoreArticoli
-
Beh che non avvisa non ha importanza sarebbe bello capire se può essere migliorato sulla velocità e lo sfarfallio. Ma su quello non penso io ci possa arrivare. Se ti va di darmi una mano io sono qui anche per imparare.
Grazie di nuovo
Ci sono poche e semplici istruzioni che permettono di disabilitare il refresh dello schermo, impedire l'esecuzione di ulteriori eventi, impostare il cursore nello stato di attesa, disabilitare il ricalcolo automatico delle formule. Nel tuo caso utilizzerei solo alcune di queste opzioni (il tempo di conversione a pdf è però ineliminabile).
All'inizio della routine le istruzioni per disabilitare lo schermo e gli eventi:
With Application .ScreenUpdating = False .Cursor = xlWait End Withe alla fine la loro riattivazione, dopo la chiusura del workbook di destinazione wb2.Close:
wb2.Close True With Application .ScreenUpdating = True .Cursor = xlDefault End With MsgBox "Operazioni terminate.", vbInformation End SubGrazie mille ho aggiunto anche il resto
Allegati:
You must be logged in to view attached files.Ok, piccola nota, le istruzioni di riattivazione dovrebbero essere le ultime prima di End Sub.
Ti posso chiedere una cosa se volessi un tasto che mandi una email con il PDF creato all'indirizzo in cella B5 tramite Gmail è possibile?
Edit by VF: sistemato errore di formattazione del post
Con Gmail la cosa non è semplicissima, ma è fattibile.
Bisogna interfacciarsi alla libreria CDO. In rete ci sono diversi esempi. Io stesso ne ho implementato qualcuno (e mi funziona qui in ufficio sia da Excel che da Access senza particolari problemi).
Per una lettura seria ti rinvio all'articolo dell'ottimo e famoso Ron de Bruin:https://www.rondebruin.nl/win/s1/cdo.htm
da cui puoi partire per capire di cosa stiamo parlando, poi per tradurre la tua esigenza specifica in VBA, puoi sicuramente provare a farlo e quindi tornare qui per discutere eventuali difficoltà.
ho trovato questo riesci ad aiutarmi?
Sub fSendThunderbird(to_email_address As String, cc_email_address As String, bcc_email_address As String, subject As String, allegato As String) 'http://forums.mozillazine.org/viewtopic.php?t=399230&highlight=&sid=2c05f35f3050c34449d0c0deaf16621a 'http://kb.mozillazine.org/Command_line_arguments_-_Thunderbird 'http://email.about.com/od/mozillathunderbirdtips/qt/Send_an_Image_Inline_Without_Attaching_It_in_Thunderbird.htm 'http://kb.mozillazine.org/Creating_complex_mails_with_inline_images Dim strCommand As String ' Command line to prepare Thunderbird e-mail Dim strTo As String ' E-mail address Dim strCC As String 'E-mail address Dim strBcc As String 'E-mail address Dim strSubject As String ' Subject line Dim strBody As String ' E-mail body Dim strAttachment As String 'Allegati Const cFormato As Integer = 1 '1: HTML 2:Plain Text strTo = to_email_address strCC = cc_email_address strBcc = bcc_email_address strSubject = subject strAttachment = allegato strBody = "Linea 1 " _ & "Linea 2" & " " _ & "Linea 3" & " " _ & " " _ & "Firma" & " " _ & " " _ & "Immagine 1 (remota)" & " " _ & "" _ & " " _ & "Immagine 2 (locale)" & " " _ & "" strCommand = "C:\Program Files\Mozilla Thunderbird\thunderbird.exe" strCommand = strCommand & " -compose to='" & strTo & "'," _ & "cc='" & strCC & "'," _ & "bcc='" & strBcc & "'," _ & "subject='" & strSubject & "'," _ & "format='" & cFormato & "'," _ & "body='" & strBody & "'," _ & "attachment='" & strAttachment & "'" Call Shell(strCommand, vbNormalFocus) Application.Wait (Now + TimeValue("0:00:03")) SendKeys "^{ENTER}", True End SubSì, ti posso capire. Non è stato semplice nemmeno per me quando sono forzatamente passato da Outlook a Gmail per lavoro. Comunque adesso ti cerco il codice che uso io e te lo posto. Naturalmente andrà aggiustato.
potrei usare anche thunderbird, ho gia collegato gmail e funziona. il codice sicuramente va modificato nella creazione di un pdf virtuale da allegare. l'ho provato ma sembra non andare
Ti allego un file che sul mio pc funziona, però devi attivare una "password per le app" attraverso il pannello di controllo di Google. Infatti Gmail considera questo accesso nu tentativo non legale di accedere alla tua casella di posta e richiede una password generata in modo sicuro.
Devi seguire la procedura di Google a questo link:
https://support.google.com/accounts/answer/185833?hl=it#:~:text=Una%20password%20per%20l'app,la%20verifica%20in%20due%20passaggi.Vedi poi nel codice del file allegato, devi modificare opportunamente le righe del mittente, del destinatario e della password per app che devi inserire materialmente nel codice.
Allegati:
You must be logged in to view attached files.allora il codice mi funziona correttamente ora ho solo una necessità. invece di prendere un file pdf in una posizione precisa posso creare un pdf virtuale (cioè che non viene salvato localmente) del foglio "output" del precedente file? ed inoltre l'indirizzo email può essere preso dalla cella B5 del foglio input?
in sommi capi io eseguo 2 macro separate la prima che è quella del post genera un pdf ed un excel! e qui abbiamo risolto
la seconda genera un pdf virtuale uguale al precedente e lo invia via email all'indirizzo che si trova in B5 nel foglio input
la seconda genera un pdf virtuale uguale al precedente e lo invia via email all'indirizzo che si trova in B5 nel foglio input
Io personalmente non sono in grado di farti creare un pdf in memoria. La soluzione semplice è crearlo in una cartella temporanea nota a priori, allegarlo alla mail e poi cancellarlo dalla cartella temporanea (di solito consultando Environ("TEMP") trovi il percorso di una cartella temporanea definita localmente e utilizzabile).
Sull'indirizzo in B5, non vedo alcun problema, basta che l'indirizzo sia valido. Quando invochi fSendThunderbird() devi passare come primo parametro il destinatario, e questo può essere benissimo un riferimento al Range("B5").Value.
Forse c'è stata un incomprensione. Guarda che io sto usando il tuo codice non il mio.
Il mio non funziona.
Allora facciamo così. Essendo che come prima macro utilizzo il salva excel e PDF. Il PDF l'ho già creato e so già dov'è. Come dire al codice che deve prendere l'ultimo file della cartella oppure quel file specifico. Perché il nome del file io lo so in tempo reale non prima quindi non posso scriverlo nel codice.
Per quanto riguarda l'email in B5 con il tuo codice come posso modificarlo?
ecco il codice ti inserisco le modifiche in verde che devo apportare:
Option Explicit 'For Early Binding, enable Tools > References > Microsoft CDO for Windows 2000 Library Sub SendEmailUsingGmail() Dim NewMail As Object Dim mailConfig As Object Dim fields As Variant Dim msConfigURL As String On Error GoTo Err: 'late binding Set NewMail = CreateObject("CDO.Message") Set mailConfig = CreateObject("CDO.Configuration") ' load all default configurations mailConfig.Load -1 Set fields = mailConfig.fields 'Set All Email Properties With NewMail .From = "mia email" 'sender .To = " 'il destinatario lo deve prendere dalla casella B5 foglio Input" 'receiver .CC = "" .BCC = "" .Subject = "Test from CDO" .Textbody = "This is a test for CDO!" .AddAttachment " 'C:\Users\Anna\Desktop\Preventivi Eurolido\PDF\la cartella di destinazione è sicuramente questa ma il nome del file lo creo in quel momento come faccio?" End With msConfigURL = "http://schemas.microsoft.com/cdo/configuration" With fields .Item(msConfigURL & "/smtpusessl") = True 'Enable SSL Authentication .Item(msConfigURL & "/smtpauthenticate") = 1 'SMTP authentication Enabled .Item(msConfigURL & "/smtpserver") = "smtp.gmail.com" 'Set the SMTP server details .Item(msConfigURL & "/smtpserverport") = 465 'Set the SMTP port Details .Item(msConfigURL & "/sendusing") = 2 'Send using default setting .Item(msConfigURL & "/sendusername") = "mia email" 'Your gmail address .Item(msConfigURL & "/sendpassword") = "password app ok" .Update 'Update the configuration fields End With NewMail.Configuration = mailConfig NewMail.Send MsgBox "Your email has been sent", vbInformation Exit_Err: 'Release object memory Set NewMail = Nothing Set mailConfig = Nothing End Err: Select Case Err.Number Case -2147220973 'Could be because of Internet Connection MsgBox "Check your internet connection." & vbNewLine & Err.Number & ": " & Err.Description Case -2147220975 'Incorrect credentials User ID or password MsgBox "Check your login credentials and try again." & vbNewLine & Err.Number & ": " & Err.Description Case Else 'Report other errors MsgBox "Error encountered while sending email." & vbNewLine & Err.Number & ": " & Err.Description End Select Resume Exit_Err End SubGuarda che io sto usando il tuo codice non il mio.
🤦♂️ scusa non avevo capito
Prima che mi dimentichi di nuovo ti devo dire che per sua natura Gmail non salva in posta inviata la mail che viene generata dal codice. Per averne una copia è necessario che te la spedisci (magari nel campo Ccn.
Per quanto riguarda l'email in B5 con il tuo codice come posso modificarlo?
Questa è la cosa più facile ovviamente perché semplicemente prelevi B5 dal foglio Input:
.To = Worksheets("Input").Range("B5").Value.AddAttachment" 'C:\Users\Anna\Desktop\Preventivi Eurolido\PDF\la cartella di destinazione è sicuramente questa ma il nome del file lo creo in quel momento come faccio?"Naturalmente tu conosci il nome del file pdf generato, non lo crei in questo momento, ma lo hai già stabilito al momento del comando ExportAsFixedFormat. Ti suggerisco di salvare il nome del file nel foglio Input, per esempio nella cella N45 (che è la prima libera sotto i dati del nome file), e poi preleverai da lì. Se il codice di cui parliamo non è cambiato dovrebbe risultare una cosa del genere:
With wb2.Worksheets("Output") .Columns("A:A").ColumnWidth = 44.57 .Range("$A$5:$C$64").AutoFilter Field:=3, Criteria1:="<>" Worksheets("Input").Range("N45") = p & "\PDF\" & Replace(s, "/", "-") & ".pdf" .ExportAsFixedFormat xlTypePDF, Worksheets("Input").Range("N45").Value End WithIn questo modo memorizzi il nome del pdf in una specie di variabile globale persistente (una cella di Excel è una specie di variabile globale persistente perchè viene salvata col foglio), cui puoi accedere successivamente al momento dell'AddAttachment:
.AddAttachment Worksheets("Input").Range("N45").ValueIl ragionamento mi sembra corretto. Non credo che troverai particolari difficoltà a implementare una cosa del genere.
Prima che mi dimentichi di nuovo ti devo dire che per sua natura Gmail non salva in posta inviata la mail che viene generata dal codice. Per averne una copia è necessario che te la spedisci (magari nel campo Ccn.
Cavolo davvero un peccato. Beh devo capire come risolverlo devo avere sempre una copia della posta inviata.
With wb2.Worksheets("Output") .Columns("A:A").ColumnWidth = 44.57 .Range("$A$5:$C$64").AutoFilter Field:=3, Criteria1:="<>"
Worksheets("Input").Range("N45") = p & "\PDF\" & Replace(s, "/", "-") & ".pdf" .ExportAsFixedFormat xlTypePDF, Worksheets("Input").Range("N45").Value End With
ho provato mettendo anche un punto davanti Worksheets:
1. senza punto genera pdf ed excel ma non scrive il nome del file in N45
2. con il punto va in errore proprio nel rigo worksheets
di conseguenza non ho potuto provare l'invio email perche non mi scrive in N45
devo capire come risolverlo devo avere sempre una copia della posta inviata.
Non si risolve, con Gmail. E' proprio un limite che non si riesce a superare (non è come Outlook dove si poteva infilare il messaggio nella cartella dedicata). L'unica è mandare a se stessi una copia della mail (funziona perché lo faccio regolarmente e non ho problemi). Il campo interessato è Ccn, che nel codice diventa Bcc, e basta inserire nel codice il tuo indirizzo mail:
'Set All Email Properties With NewMail .From = "Nome Cognome <la_tua_email@dominio.it>" ' fa apaprire Nome e Cognome nella mail .To = Worksheets("Input").Range("B5").Value .CC = "" .BCC = "la_tua_email@dominio.it" ' invia una copia a me stesso per tener traccia della mail inviataho provato mettendo anche un punto davanti Worksheets:
bè no, non puoi mettere dei punti a caso 🙂
Il punto è una notazione speciale che serve a riferirsi a un membro di un oggetto (metodo o proprietà). Il With serve a evitare di scrivere svariate volte il riferimento a un oggetto padre, riferendosi ad esso per tuttala durata del blocco With.
Probabilmente nel codice ho dimenticato di specificare di impostare la proprietà Value del range N5; correggi così:Worksheets("Input").Range("N45").Value = p & "\PDF\" & Replace(s, "/", "-") & ".pdf"Ho ricontrollato il codice ed effettivamente il foglio Input da modificare non è quello di cui stai creando il pdf ma quello di aprtenza.
Perciò la riga corretta da scrivere è:
wb1.Worksheets("Input").Range("N45").Value = p & "\PDF\" & Replace(s, "/", "-") & ".pdf"Come vedi il Worksheet è qualificato dal suo corretto Workbook, adesso, e funzionerà come ci si aspetta.
allora piccoli problemi risolvibili:
1. il file adesso copia il percorso in N45 ma non so perchè il cursore rimane impegnato nonostante abbia finito. Se io mi metto a fare un altra operazione lui va cmq pero invece di uscire la freccetta esce rotellina come da immagine.jpg
2. la macro email mi da questo errore? errore email.jpeg
3. ti ho allegato un file pulito con entrambe le macro se vuoi fare qualche prova con il mio. ho soltanto eliminato le informazioni sensibili contrassegnate con "xxx", cioè:
email destinario in B5
nel codice:
email mittente
password
Allegati:
You must be logged in to view attached files.Quanto ad 1), mi autocito...
e alla fine la loro riattivazione, dopo la chiusura del workbook di destinazione wb2.Close:
wb2.Close True With Application .ScreenUpdating = True .Cursor = xlDefault End With MsgBox "Operazioni terminate.", vbInformationEnd Sub...quando hai terminato tutto, devi ripristinare il cursore allo stato iniziale.
Quanto ad 2), è strano perchè l'etichetta Err mi sembra definita (fermo restando che il nome dell'etichetta non è dei più felici, visto che Err è un oggetto riservato). Puoi cambiarla con xErr o qualsiasi altra cosa (purchè ne cambi le occorrenze in tutto il codice).
Adesso guardo il file che hai allegato 🙂
1. Io ho tolto la casellina terminato perché per me è un passaggio in più che non serve. Anche perché si vede quando finisce perché esce la pubblicazione del PDF. Possiamo fare che l'operazione terminata non esce e rimane sempre la freccetta?
Ottimo, hai messo un bel pulsante che fa tutto e funziona perfettamente
La parte della mail la devo provare a casa perchè qui in ufficio non posso generare una password per app, il pc non è mio 🙂 (la password per app viene generata per e sul dispositivo autorizzato).
Possiamo fare che l'operazione terminata non esce e rimane sempre la freccetta?
Come hai fatto tu va benissimo, ma devi ripristinare il cursore al termine, ho visto che non hai messo le istruzioni finali:
With wb2.Worksheets("Output") .Columns("A:A").ColumnWidth = 44.57 .Range("$A$5:$C$64").AutoFilter Field:=3, Criteria1:="<>" wb1.Worksheets("Input").Range("N45").Value = p & "\PDF\" & Replace(s, "/", "-") & ".pdf" .ExportAsFixedFormat xlTypePDF, Worksheets("Input").Range("N45").Value End With wb2.Close True With Application .ScreenUpdating = True .Cursor = xlDefault End With End SubQuesta è l'unica parte che hai dimenticato di inserire.
Quanto ad 2), è strano perchè l'etichetta Err mi sembra definita
Ah ma tu nel tuo codice hai ELIMINATO tutta la gestione degli errori...io l'avrei lasciata. Ma se non ti serve, allora puoi togliere del tutto l'istruzione On Error Goto Err... toglila e tutto filerà liscio senza problemi di etichette 😉
allora l'ho aggiustato per come serviva ed adesso funziona alla perfezione. ho un ultimo problema la formattazione del testo email. io devo aggiungere al corpo email questo testo cosi come lo vedi ed ho provato ad incollarlo ma me lo sfalsa e mi da errore come devo fare per aggiustarlo come si deve? ti allego immagine:
La ringraziamo per l'attenzione e Le inviamo in allegato il preventivo per il periodo richiesto.
Dato che la Sua richiesta è stata elaborata in prossimità del giorno di scadenza delle offerte attualmente in corso, Le comunichiamo che nel caso volesse confermare potrà farlo procedendo al versamento della caparra entro lunedì 30 gennaio; in tal caso riterremo valido il preventivo inviatoLe con le offerte ed i prezzi applicati in data odierna (nonostante nel preventivo sia riportata come data di scadenza il 27 gennaio).
Un infant 0/2 anni compiuti in camera con due adulti è sempre gratuito senza posto letto, con pasti da menù inclusi; è possibile prenotare la culletta per gli infant al costo di 50 € a settimana (per motivi di sicurezza non è consentito utilizzare la culla propria). È presente la biberoneria (sala-cucina attrezzata in cui gli ospiti possono gratuitamente preparare i pasti per gli infant; gli alimenti non sono forniti dalla struttura, ma devono essere procurati direttamente dagli ospiti).
Il prezzo totale, come vedrà dettagliatamente nel preventivo allegato, comprende:
Soggiorno con trattamento di pensione completa con acqua e vino inclusi ai pasti (il pacchetto inizia con la cena del giorno di arrivo e termina con la colazione del giorno di partenza).
Tessere club per usufruire dei servizi spiaggia (raggiungibile direttamente dall'interno del villaggio tramite sottopassaggio privato), piscina, animazione, etc.
Per la sistemazione in camera lato mare è previsto un supplemento di € 70,00 a settimana.
La distinzione tra lato mare e lato monte riguarda esclusivamente il panorama visibile dalla camera; la distanza dalla spiaggia è equivalente per entrambe le tipologie di camera.La invitiamo a visitare il nostro sito internet per ulteriori informazioni sulla struttura e sui servizi offerti.
Per qualsiasi informazione aggiuntiva può contattarci rispondendo a questa email, telefonicamente al n° o tramite WhatsApp al n° .
La ringrazio e resto a Sua disposizione.
Cordialmente,
Francesca
Allegati:
You must be logged in to view attached files.Ah ho capito... no, in VBA non funziona così per andare a capo nel codice. Ci sono diverse soluzioni.
1) chiudi la stringa con le virgolette e concatenala alla successiva, con l'operatore &, badando a mettere un underscore "_" che significa "l'istruzione prosegue alla riga successiva".
Esempio:.TextBody = "La ringraziamo per l'attenzione e Le inviamo in allegato il preventivo per il periodo richiesto. " & vbnewline & _ "Dato che la Sua richiesta è stata elaborata in prossimità del giorno di scadenza delle offerte " & _ "attualmente in corso, Le comunichiamo che nel caso volesse confermare potrà farlo procedendo al " & _ "versamento della caparra entro lunedì 30 ..."2) concateni più stringhe ad una stringa mediante l'operatore &, assegnando ogni volta alla stessa stringa il contenuto di se stessa:
.TextBody = La ringraziamo per l'attenzione e Le inviamo in allegato il preventivo per il periodo richiesto." & vbnewline .TextBody = .TextBody & "Dato che la Sua richiesta è stata elaborata in prossimità del giorno di scadenza " .TextBody = .TextBody & "delle offerte attualmente in corso, Le comunichiamo che nel caso volesse " .TextBody = .TextBody & "confermare potrà farlo procedendo al versamento della caparra entro lunedì 30..."3) prendi una cella del foglio di Input, ci scrivi il testo integrale, e lo assegni a .TextBody. Poniamo che la cella N50 contenga il testo:
.TextBody = wb1.Worksheets("Input").Range("N50")Di passaggio faccio notare che stai parlando di solo testo piano, e che non hai la possibilità di inserire formattazioni come grassetti o colori. Per avere questi effetti devi assegnare il testo alla proprietà .HTMLBody, ma il contenuto va messo in formato HTML quindi devi avere dimestichezza con questo ultimo per impostare correttamente le formattazioni.
-
AutoreArticoli
