Invio promemoria fatture



  • Invio promemoria fatture
    di Mik data: 09/07/2017 16:30:54

    Ciao ragazzi/e,
    avrei bisogno del vostro aiuto per mettere insieme una macro un pò complicata (almeno per me)...
    Ecco l'idea a cui voglio arrivare:
    Mandare una mail ad ognuno dei miei clienti per riepilogare la fattura/le fatture in scadenza, le mail possono essere di due tipi in base a se il cliente ha una fattura in scadenza o più fatture.
    Finchè si tratta di clienti con una fattura ho risolto, il mio problema nasce quando il cliente ha più fatture in scadenza.

    Il mio problema diventa quando devo fargli capire di confrontarmi il codice del cliente della riga 1 con tutte le altre righe per ragggrupparmi tutte le fatture scadute in un unica mail... Ho provato a gestire con la funzione "For" ma ho qualche difficoltà.

    Vi allego un file di esempio, spero riusciate ad aiutarmi sono in un punto morto.
    Grazie in anticipo per l'aiuto!!
     
    Sub PromemoriaInvioFatture()
    
    'dati base
    Dim RagioneSociale As String
    Dim IndirizzoEmail As String
    Dim BodyMsg As String
    Dim Oggetto As String
    Dim PercorsoFile As String
        'dati utili con clienti monofatture
        Dim x As Variant
        Dim CodClie As Variant
        Dim NrFattura As String
        Dim ImpFattura As Currency
        Dim DataFattura As Date
            'dati utili con clienti con più fatture
            Dim y As Variant
            Dim CodClie2 As Integer
            Dim NrFattura2 As String
            Dim ImpFattura2 As Currency
            Dim DataFattura2 As Date
    
        For x = 2 To 10
       
        y = x + 1
        
        RagioneSociale = Worksheets("Foglio1").Cells(x, 2)
        IndirizzoEmail = Worksheets("Foglio1").Cells(x, 6)
        CodClie = Worksheets("Foglio1").Cells(x, 1)
        NrFattura = Worksheets("Foglio1").Cells(x, 3)
        ImpFattura = Worksheets("Foglio1").Cells(x, 4)
        DataFattura = Worksheets("Foglio1").Cells(x, 5)
                    
        CodClie2 = Worksheets("Foglio1").Cells(y, 1)
        NrFattura2 = Worksheets("Foglio1").Cells(y, 3)
        ImpFattura2 = Worksheets("Foglio1").Cells(y, 4)
        DataFattura2 = Worksheets("Foglio1").Cells(y, 5)
                 
          If CodClie <> "" Then
                
                If CodClie = CodClie2 Then
                
                    
                    BodyMsg = "Gentile Cliente," _
                    & vbCrLf & "con la presente siamo a ricordarLe che oggi sono in scadenza le fatture:" _
                    & vbCrLf & "-Nr. " & NrFattura & " del " & DataFattura & " per € " & ImpFattura & ";" _
                    & vbCrLf & "-Nr. " & NrFattura2 & " del " & DataFattura2 & " per € " & ImpFattura2 & "." _
                    & vbCrLf & "Cordiali saluti."
                    
                    Oggetto = "Promemoria scadenza fatture"
                    
                        If IndirizzoEmail <> "" Then
                        Shell "C:Program Files (x86)Mozilla Thunderbird	hunderbird - compose" _
                        & Chr$(34) & "to='" & IndirizzoEmail & "', subject='" & Oggetto & "', body '" & BodyMsg _
                        & "', attachment = '" & PercorsFile & "'" & Chr$(34), vbNormalFocus
                        Application.Wait Now + TimeValue("00:00:03")
                        End If
                
                Else
                                
                    BodyMsg = "Gentile Cliente," _
                    & vbCrLf & "con la presente siamo a ricordarLe che oggi è in scadenza la fattura nr. " & NrFattura & " del " & DataFattura & " per € " & ImpFattura & ";" _
                    & vbCrLf & "Cordiali saluti."
                    
                    Oggetto = "Promemoria scadenza fatture"
                    
                        If IndirizzoEmail <> "" Then
                        Shell "C:Program Files (x86)Mozilla Thunderbird	hunderbird - compose" _
                        & Chr$(34) & "to='" & IndirizzoEmail & "', subject='" & Oggetto & "', body '" & BodyMsg _
                        & "', attachment = '" & PercorsFile & "'" & Chr$(34), vbNormalFocus
                        Application.Wait Now + TimeValue("00:00:03")
                        End If
                
                End If
        
        End If
        
        Next x
        
        
    End Sub



  • di Vecchio Frac data: 09/07/2017 18:05:24

    Ho aperto il file, permettimi due osservazioni:
    - la prima è che dovresti abituarti a usare sempre Option Explicit in testa ai tuoi codici (puoi rendere obbligatoria la dichiarazione di variabili in Editor >> Strumenti >> Opzioni >> Dichiarazione di variabili obbligatoria)
    - la seconda è che trovo un errore di disegno considerare i clienti con una fattura diversi da quelli con più fatture... il cliente è di un solo tipo. Uno dei suoi attributi è avere una o più fatture, semplicemente. Inutile dedicare tante variabili diverse per indicare gli stessi tipi di oggetto.

    Dovresti ristudiare la logica (l'approccio, l'algoritmo, come preferisci chiamarlo).
    L'invito che ti faccio è scrivere in italiano i passi necessari a raggiungere l'obiettivo come se tu dovessi spiegarlo a chi non sa niente del tuo problema (o, che è lo stesso, a Excel). Alla fine è solo un problema di raggruppamento dei clienti per codice :)





  • di Vecchio Frac data: 09/07/2017 21:36:33

    BTW ho riscritto il tuo codice, e dovrebbe funzionare, va testato (però volevo avere prima un tuo riscontro).





  • di Mik data: 10/07/2017 08:20:31

    Ciao Vecchio Frac,
    In una delle miei precedenti discussioni mi avevi consigliato di dichiarare tutte le variabili ed ero convinto di averlo fatto questa volta, però non ho minimante idea di cosa sia Option Explicit quindi staserà vedrò di studiarmela un attimo; quindi intanto grazie....
    Per quanto riguarda il progetto provo ad essere più specifico:
    1- Dal mio gestionale di Access ho un estrazione dalla quale fuoriesce la lista di clienti che hanno fatture in scadenza in giornata;
    I miei clienti possono avere una sola fattura o anche 100 fatture in scadenza lo stesso giorno (questa particolaritá non é fissa per ogni cliente ma varia costantemente, per spiegarmi meglio oggi il cliente Paolo Rossi ordina solo merce x e decide che vuole una fattura complessiva per tutta la merce ordinata ma magari fra un mese decide di volere acquistare anche la merce y e quindi vuole due fatture distinte per merce questo può capitare fino ad un numero impreciso di merci diverse);
    2- Copio la lista di dati in excel e ordino i miei clienti per codice cliente (Codice univoco, ad ogni cliente corrisponde uno e uno solo codice cliente).

    Questa è la partenza del progetto, dato che il mio obbiettivo finale é quello di mandare una mail dove riepilogo al cliente le fatture scadute ho pensato che la prima cosa era creare due tipologie di mail dato che deve essere strutturata in due modi diversi a seconda che il cliente sia "monofattura" o "plurifattura" ( da qui ho pensato alla funzione "IF...Then/ Else").

    Per far capire ad excel se si tratta di un cliente con piu fatture pensavo di fargli fare il confronto tra due codici clienti ( se codice della prima riga= codice riga 2 vuole dire che ha piu fatture in caso contrario 1 sola fattura) ma qui trovo qualche falla. Prima tra tutte il fatto che riesco a controllare solo 2 righe alla volta, seconda utilizzando il for una volta che mi ha controllato se la riga 1 è uguale alla riga 2 nasce un nuovo problema facendoglio fare il next dovrebbe capire in automatico di ripartire dalla prima riga diversa dalle precendi; non so se mi sono spiegato intendo dire che se il primo cliente ha 10 fatture dopo la macro dovrebbe ragionare partendo dall'11 riga e non piu dalla 2...


    Non riesco a capire cosa intendi dicendo di non considerare i clienti con una sola fattura diversi da quelli con piu fatture(??), come posso fare per considerarli uguali ma allo stempo tempo dividere le due casistiche?

    Quando mi parli di problema di raggruppamento tra codice cliente intendi che conviene valutare l'opzione di una pivot??

    Come sempre, grazie del sostegno



  • di Vecchio Frac data: 10/07/2017 09:08:08

    Per intanto su Option Explicit ti segnalo questo post di scossa:
    excelvba.it/Forum/story/Excel_e_gli_applicativi_Office/Dichiarazione_delle_variabili_e_Option_Explicit_.html

    Sul resto del tuo messaggio: se hai già il gestionale in Access, ottima cosa, perchè non fai tutto da lì e risparmi di impazzire con Excel? :)
    Comunque ho letto il tuo codice e ho capito come affronti il problema. Non sarebbe neanche male ma io opto per una soluzione più versatile :)
    Io per risolvere il problema del raggruppamento per codice cliente ho utilizzato un oggetto Dictionary dove ho inserito i dati multipli come stringhe suddivise da un separatore, che poi vado a recuperare in fase di costruzione della mail:
    cliente 1|fattura 1|importo|data scadenza||fattura 2|importo|data scadenza||fattura 3|importo|data scadenza ecc.
    Quando dicevo di considerare i clienti con una fattura uguali a quelli con più fatture intendevo dire solo che il "tipo" cliente è uno solo, che ha solo la caratteristica di avere uno o più valori da associare al campo "numero fattura". L'uso del Dictionary permette questo... ok, dovremmo costruire un vero tipo oggetto con un modulo di classe ma non mi pare il caso di esagerare.

    (Capisco che detta così sembra complicata o difficile, ma in pratica non lo è)





  • di Mik data: 10/07/2017 09:32:22

    A livello teorico ho capito più o meno il ragionamento anche se non ho mai affrontato le Dictionary; in serata voglio provo a buttare giù un idea di macro per cercare di capire meglio come funziona... Alla peggio ti chiederò qualche supporto.

    Per quanto riguarda Access, immaginavo fosse meglio fare tutto da li ma per il momento io lavoro per lo piu' con la creazione in struttura e non in SQL dove ho ancora qualche difficoltà spero col tempo di imparare a gestire anche access



  • di Vecchio Frac data: 10/07/2017 11:17:20

    Ma se può aiutarti, il VBA è esattamente lo stesso nei due ambienti. Cambiano un po' i modelli ad oggetti ma se mastichi lo sviluppo in Excel sei a posto anche in Access. Che io preferisco di gran lunga :) (e non è neanche strettamente necessario SQL a meno di non voler fare delle cose un po' più impegnative)

    Io non voglio farti impazzire su un problema apparentemente semplice anche perchè ho già scritto il codice che ti serve quindi appena ho un attimo ti carico qui il file di esempio; io voglio solo che i miei utenti imparino ad affrontare i problemi (e a vedere le soluzioni) con un certo criterio ragionato, senza doversi affidare sempre a terzi che poi li lasciano lì con soluzioni pronte ma di cui capiscono poco :)






  • di Mik data: 10/07/2017 14:03:54

    Per il codice, ovviamente se lo hai già disponibile schifo non fa trattandosi di questioni di lavoro riesco a recuperare un pò di tempo ma in ogni caso ormai il linguaggio VBA è diventato un interesse/hobby per me e sto cercando di documentarmi il più possibile a riguardo quindi come ti dicevo in serata provo a modificare aggiungendo Option Explicit e provo studiarmi un pò il discorso delle Dictionary tanto in futuro potrebbero sempre essere utili!!!



  • di alfrimpa data: 10/07/2017 14:41:29

    Mik forse ti può essere utile questo link sperando che un po' di inglese lo mastichi (ma in italiano su argomenti appena appena un po' particolari non si trova nulla).

    h t t p s://www.experts-exchange.com/articles/3391/Using-the-Dictionary-Class-in-VBA.html

    Alfredo





  • di Vecchio Frac data: 10/07/2017 14:41:41

    cit. "schifo non fa"
    ---> Lol

    Posto qui il codice. E' tarato sul file di esempio che hai già mandato. Tutto va incollato in un modulo nuovo.
    E' in fase di test quindi non spedisce niente via mail (infatti il codice che lo fa è commentato, nella Function "compose_mail"). però faccio comparire un msgbox che ti illustra il codice per la spedizione, con tutti i dati che verrebbero inviati a Thunderbird.
    Poi se hai pazienza e voglia il codice lo rivediamo insieme.
     
    Option Explicit
    
    Sub PromemoriaInvioFatture_VF()
    Dim dict As Object
    Dim v As Variant
    Dim cel As Range
    Dim s As String, p As String
    
        Set dict = CreateObject("Scripting.Dictionary")
        
        For Each cel In Range("A1").CurrentRegion.Rows
            If cel.Row > 1 Then
                s = CStr(cel.Cells(1))
                v = Range(Cells(cel.Row, "B"), Cells(cel.Row, "F"))
                v = Application.Transpose(Application.Transpose(v))
                If Not dict.exists(CStr(s)) Then
                    dict.Add CStr(s), Join(v, "|")
                    p = ""
                Else
                    p = dict(s) & "^|" & Join(v, "|") & "^|"
                    dict(s) = Left(p, Len(p) - 2)
                End If
            End If
        Next
        
        For Each v In dict
            s = compose_mail(dict(v))
        Next
    End Sub
    
    
    Private Function compose_mail(s As String)
    Dim v As Variant, vv As Variant, k As Variant
    Dim bodymsg As String
    Dim oggetto As String
    Dim destinatary As String
    Dim percorsofile As String
    
        v = Split(s, "^|")
        If UBound(v) > 0 Then   'più fatture
            bodymsg = "Gentile Cliente,§con la presente siamo a ricordarLe che oggi sono in scadenza le fatture:§"
            For Each vv In v
                k = Split(vv, "|")
                bodymsg = bodymsg & "- Nr. " & k(1) & " del " & k(3) & " per " & k(2) & ";§"
                destinatary = destinatary & k(4) & ";"
            Next
        Else
            vv = Split(s, "|")
            bodymsg = "Gentile Cliente,§con la presente siamo a ricordarLe che oggi è in scadenza la fattura nr. " & _
                      vv(1) & " del " & vv(3) & " per " & vv(2) & ";§"
            destinatary = vv(4)
        End If
        
        bodymsg = bodymsg & "§Cordiali saluti."
        bodymsg = Replace(bodymsg, "§", vbNewLine)
        
        oggetto = "Promemoria scadenza fatture"
    
        percorsofile = ""
    
        If destinatary <> "" Then
            s = "C:Program Files (x86)Mozilla Thunderbird	hunderbird - compose" _
            & Chr$(34) & "to='" & destinatary & "', subject='" & oggetto & "', body '" & bodymsg _
            & "', attachment = '" & percorsofile & "'" & Chr$(34)
            
            MsgBox "Invio mail" & vbNewLine & s, vbInformation
            
            'Shell s, vbNormalFocus
            'Application.Wait Now + TimeValue("00:00:03")
    
        End If
    
    End Function
    
    






  • di Vecchio Frac data: 10/07/2017 14:42:05

    @alfrimpa
    +1





  • di Mik data: 10/07/2017 16:12:27

    Innanzitutto: WOWWWWWWWW, Vecchio Frac ho dato una letta veloce al codice ed è spaziale; dopo con più calma me lo analizzo un pò e provo a capirlo meglio!

    alfrimpa grazie mille del link, accetto tutti gli spunti, dopo provo a guardare meglio anche quello!

    Vi aggiorno su come procede il tutto, grazie mille



  • di alfrimpa data: 10/07/2017 16:52:22

    Vecchio Frac non scrive codice (quello lo fanno i comuni mortali) fa magie.


    Alfredo





  • di Vecchio Frac data: 10/07/2017 16:58:48

    Ma esagerati entrambi :)
    Prima provatelo e poi mi direte quali bug saltano fuori (è sempre così).
    Comunque in questi giorni ho la testa su Implements (interfacce e polimorfismo) per un problemino carino che probabilmente (nella forma più semplice, senza polimorfismo) prima o poi condividerò anche qui. Il punto è che articoli in italiano non ce ne sono molti e mi tocca leggere tutto in inglese, e io faccio una grande fatica.






  • di Mik data: 10/07/2017 21:29:32

    Vecchio Frac eccomi pronto a testare la macro e nascono i primi problemi (per lo più legati alla comprensione) spero tu possa darmi una mano a tradurre alcune parti:
    - "For Each cel in Range("A1").CurrentRegion.Rows" ; che cosa significa?? da quanto mi pare di aver letto online serve per definire un rettangolo attorno al campo A1, ma come faccio a capire di che dimensioni è il rettangolo? ma sopratutto per quale motivo lo utilizzi? Per definire i campi da prendere in considerazione?

    - "v = Application.Transpose(Application.Transpose(v))"; da quanto ho capito è utilizzata per trasporre i valori da una cella, da v=(X, Y) a v=(Y, X), corretto? ma quindi a cosa serve nel mio caso?

    - "Join(v, "|") ", questa non capisco... stai cercando di unire la variabile v con cosa?

    - " p = dict(s) & "^|" & Join(v, "|") & "^|" "; il fatto che non capisca questa secondo me deriva dal fatto che non capisco la precedente

    - v = Split(s, "^|") ; questo da quanto mi pare di capire sarebbe la funzione opposta di Join?

    - Ubound(v) ; è la funzione che ti permette di capire se il cliente ha più fatture, giusto?

    - §, noto che lo utilizzi nel BodyMsg; lo utilizzi per richiamare una parte del messaggio?

    - nel mio mini manuale di Vba non si parla di Dictionary, quindi sto cercando una qualche guida online ma al momento non ho trovato nulla di soddisfacente;

    So che le richieste sono tante, ma come dicevi tu sono interessato a capire il funzionamento della macro così che nel momento in cui debba rimetterci mano almeno posso orientarmici e capirla al volo e poi ripeto è davvero spaziale, non mi sarei mai sognato di arrivarci da solo purtroppo sono ancora troppo indietro con gli studi per creare una creatura del genere.
    Ma per capire se ho compreso più o meno il funzionamento della macro tu hai diviso in due i processi: 1- individua se si tratta di cliente monofattura o di cliente plurifattura; 2- gli fai comporre la mail in base a quanto indentificato prima. Così facendo eviti quell'inutile ripezioni che ero costretto ad utilizzare io prima, questo mi pare il ragionamento di base poi il succo all'interno ho ancora qualche difficoltà ad identificarlo...



  • di Vecchio Frac data: 10/07/2017 21:58:05

    Adesso sto per spegnere ma spero domani di poter scriverti un bel post chiarificatore.
    L'unica cosa che puntualizzo adesso riguarda l'oggetto Dictionary: alfrimpa ti ha già suggerito un link da leggere, dagli un'occhiatina anche se è in inglese :)





  • di Vecchio Frac data: 11/07/2017 15:52:22

    Spero di non annoiarti con questo lungo pistolotto :)

    Il codice proposto scandisce riga per riga la tabella dati.
    All'inizio prepara in memoria un oggetto dizionario vuoto (per riempirlo devi pensare a una matrice dove immetti i valori abbinati tra loro nella forma 'chiave = valore', per esempio cognome = "pippo")
    Di questa riga ottiene un riferimento delle celle da B a F, ignorando la colonna A perchè contiene il codice cliente che non è un dato da conservare nei valori bensì nelle chiavi del dizionario. I valori delle celle da B a F vengono uniti con Join per ottenere una stringa unica in cui i valori delle diverse celle vengono separati dal carattere "|" (pipe: ho scelto questo carattere perchè è difficile usarlo in produzione: nei tuoi dati però non dovrà mai comparire questo carattere). Ad esempio "1|300,00|11/07/2017". Questo valore viene associato alla chiave "codice cliente" che trovasi nella cella A.
    Nei cicli successivi si fa un controllo: se esiste già la chiave "codice cliente" allora basta aggiungere il nuovo valore della riga joinata al valore precedente per lo stesso cliente, altrimenti (se non esiste già la chiave codice cliente) si tratta di un cliente nuovo e quindi si crea una nuova voce del dizionario con questa chiave. Se aggiungo un valore nuovo ad un cliente esistente, lo accodo alla stringa valore già associata al cliente e siccome devo distinguerla la unisco alla stringa con dei caratteri separatori ("^|": accento circonflesso e pipe) scelti in modo arbitrario e improbabili (anche qui, la coppia di caratteri indicata non deve comparire nei tuoi dati).
    Alla fine del ciclo avrai per ogni cliente un elemento del dizionario, in cui la chiave è il suo codice cliente e il valore è una lunga stringa.
    Il risultato è che il dizionario è così costruito (per es.):
    dict ("11111") = "1|300,00|11/07/2017"
    dict ("11112") = "2|150,00|07/07/2017^|3|250,00|10/10/2017"
    Da cui capisci che la chiave 11111 (cliente 11111) ha una sola fattura, i cui campi sono separati dalla pipe "|".
    Invece la chiave 11112 (cliente 11112) ha due fatture, separate dalla coppia di caratteri "^|"; ognuna delle due sottostringhe (separate dalla coppia di caratteri vista) individua regolarmente i campi come sopra.

    Adesso che abbiamo un dizionario riempito coi valori che ci servono (e che riflette la struttura della tabella originale ma è più masticabile), dobbiamo comporre la mail per i destinatari. Per fare questo ho preferito (per motivi di ordine concettuale) creare una routine separata. E' una Function ma potrebbe essere una Sub (dovrebbe esserlo in realtà, perchè non ci aspettiamo alcun valore di ritorno).
    Innesco un ciclo cliente per cliente (quindi: per ogni chiave del dizionario) e richiamo la routine passandole come parametro il valore associato alla chiave che sto esaminando in quel momento. Quindi la Function adesso sta esaminando un valore stringa al cui interno si trovano i campi della riga cliente separati da "|" (e, se ci sono più righe per il cliente, le diverse righe sono separate dalla coppia "^|").
    Al suo interno la Function cerca proprio innanzitutto se si tratta di un valore multiriga. Quindi usa Split per separare la stringa fornita in più sottostringhe in corrispondenza della coppia di caratteri ^|. Split deve essere assegnato a una variabile variant che diventa un array a un indice a base zero; perciò se il valore massimo di elementi (contato con UBound) è maggiore di zero significa che ci sono almeno due sottostringhe separate da ^| nella stringa originale: e quindi ci sono almeno due fatture. L'If testa questa condizione e prepara il corpo del messaggio con le giuste diciture. I singoli campi sono prelevati da ogni singola sottostringa, esaminandole tutte con un altro ciclo che splitta questa volta sul carattere "|", recuperando ogni singolo campo (i quali sono inseriti da Split in un array che parte da zero per cui array(0) è il numero fattura, array(1) è l'importo, array(2) è la data, ecc.).

    Alla fine della Function posso finalmente trattare l'invio del messaggio vero e proprio.
    Costruisco la stringa "s" da dare in pasto al mio gestore di posta elettronica preferito con Shell (nell'esempio l'istruzione è commentata e al suo posto c'è un msgbox che fa vedere cosa verrebbe eseguito se fosse eseguito davvero).

    ----------
    Rispondo infine alle tue domande:
    >> For Each cel in Range("A1").CurrentRegion.Rows
    CurrentRegion individua l'area contigua attorno a una cella finché non trova una colonna vuota e una riga vuota. Quindi se la tabella inizia da A1 individua rapidamente tutta la tabella dati per la sua estensione. La riga di codice indicata innesca un ciclo che esamina ogni riga di questa regione (la variabile l'ho chiamata "cel" ma avrebbe potuto chiamarsi "riga" per chiarezza)

    >> v = Application.Transpose(Application.Transpose(v))
    Questo è un trucco interessante per appiattire un range di valori e assegnarlo a una matrice Variant; il doppio Transpose serve prima per appiattire le colonne e poi le righe a una sola dimensione (ogni oggetto Range ha due dimensioni, un indice di riga e uno di colonna ma a noi serve un vettore monodimensionale)

    >> Join(v, "|")
    Crea una nuova stringa generata dalla unione (Join) degli elementi contenuti nell'array v con il carattere pipe "|". In pratica ogni elemento dell'array finisce in una stringa con un carattere | a separarlo dagli altri elementi dell'array. Così Join(Array("pippo", "pluto", "topolino"), ":") produce la stringa "pippo:pluto:topolino".

    >> p = dict(s) & "^|" & Join(v, "|") & "^|"
    Assegna alla variabile stringa "p" il valore contenuto nella chiave "s" del dictionary più la coppia "^|" più la stringa generata dall'unione delle celle della riga in esame separate da "|" più di nuovo la coppia "^|". In sostanza siamo nel punto in cui viene rilevato che esiste già un cliente inserito in precedenza: dobbiamo assegnarli un nuovo valore di riga senza perdere il precedente valore e dobbiamo utilizzare un arbitrario separatore di riga (^|) per poter poi discriminare successivamente che si tratta di un "a capo" per lo stesso cliente.

    >> v = Split(s, "^|") ; questo da quanto mi pare di capire sarebbe la funzione opposta di Join?
    Sì: spezza la stringa s in tante sottostringhe in corrispondenza del doppio carattere "^|" e assegna le sottostringhe all'array v (quindi in v(0) hai il primo elemento, in v(1) il secondo e così via).

    >> Ubound(v) ; è la funzione che ti permette di capire se il cliente ha più fatture, giusto?
    Nel nostro caso sì; in generale la funzione UBound recupera il numero di elementi contenuto in un array (ricordando che l'array comincia da zero se non diversamente specificato da Option Base).

    >> il " (carattere di paragrafo) noto che lo utilizzi nel BodyMsg; lo utilizzi per richiamare una parte del messaggio?
    No, è un carattere arbitrario al quale successivamente sostituisco (con Replace) un carattere di ritorno a capo (vbNewLine): bodymsg = Replace(bodymsg, """, vbNewLine).
    Uso questo trucco per non rovinarmi la vista con troppi "& vbCrLf & " che mi spezzano il testo mentre lo leggo.

    >> nel mio mini manuale di Vba non si parla di Dictionary, quindi sto cercando una qualche guida online ma al momento non ho trovato nulla di soddisfacente;
    Ti rimando al link già fornito da alfrimpa.






  • di Mik data: 11/07/2017 15:57:32

    grazie infinite della spiegazione e tranquillo non mi hai annoiato anzi incuriosito ancora di più adesso devo imparare ad usarla il prima possibile!!
    In ogni caso confermo l'idea che al momento con le mie conoscenze in materia creare una macro di questo livello era e, purtroppo, è ancora fuori discussione... Detto ciò per il momento mi limiterò ad utilizzare la tua come spunto per gestire il mio problema.

    Per il momento ho provato a farla girare e ovviamente funziona benissimo tranne per una cosa: ho fatto la modifica per fare in modo che mi crei la mail invece che il MsgBox e crearla la crea ma non inserisce il bodymsg.... appena ho un secondo di tranquillità provo a capire dov'è il problema e possibilmente a risolverlo.

    Intanto come sempre GRAZIE!
    PS: ieri sera ho provato a guardare il link di alfrimpa ma essendo un argomento abbastanza impegnativo e per di più in inglese ho qualche difficoltà in qualche modo ce la farò



  • di Luca73 data: 11/07/2017 20:36:05

    Ciao
    a tutti
    Grazie VF per la spiegazione, penso di aver imparato almeno due o tre cose nuove.
    Una domanda cosa intendi per "appiattire un range di valori"?
    Ciao
    Luca





  • di Vecchio Frac data: 11/07/2017 22:49:32

    @Mik
    Mah non so se a te funzionava, io ho preso pari pari il tuo codice.
    Probabilmente la modifica è da fare qui:
    & oggetto & "', body='" & bodymsg
    perchè manca il segno di uguale analogamente a quanto fatto per to, subject, eccetera.

    @Luca73
    Infatti quella su Transpose non è una spiegazione molto tecnica vero? però rende l'idea. In pratica abbiamo un range, che è un oggetto per Excel ma un array di valori per VBA, a due dimensioni, e noi vogliamo renderlo a una dimensione. "Appiattire" in quel senso :)
    Se in A1:C1 scrivo i valori "a", "b" e "c", e in A2:C2 scrivo "aa", "bb", "cc" faccio queste trasformazioni magiche del range in un array:
    (vado in finestra immediata)
    Per una variabile v Variant dimensionata da v(1) a v(2) che contiene i valori di colonna:
    v = application.transpose(range("A1:A2"))
    
    ?v(1), v(2)
    a aa

    Per una variabile v Variant dimensionata da v(1) a v(2) che contiene i valori di riga ci vuole un doppio Transpose:
    v = application.transpose(application.transpose(range("A1:C1")))
    
    ?v(1), v(2), v(3)
    a b c


    Ci sarebbe da semplificarsi la vita con Match e Index... magari un giorno scrivo un post su questo ^_^





  • di Vecchio Frac data: 11/07/2017 22:52:11

    Errata corrige:
    cit. VF "Per una variabile v Variant dimensionata da v(1) a v(2) che contiene i valori di riga ci vuole un doppio Transpose:
    v = application.transpose(application.transpose(range("A1:C1")))"

    --> Ovviamente si intende dimensionata da v(1) a V(3) (perchè sono tre i valori del range A1:C1, è chiaro) ^_^





  • di Luca73 data: 12/07/2017 16:17:00

    Ci ho messo un po' ma forse ho capito
    nell'esempio sotto v1 è un vettore di una dimensione con tre elementi
    v2 è una matrice in due dimensioni di cui una ha un elemento e l'altra 3

    pertanto nel primo caso chiamerò v1(x)
    nell'altro caso v2(1;x)

    Corretto?
     
    v1 = Application.Transpose(Application.Transpose(Range("A1:C1")))
    v2 = Range("A1:C1")
    






  • di Vecchio Frac data: 12/07/2017 19:52:39

    cit. "pertanto nel primo caso chiamerò v1(x)
    nell'altro caso v2(1;x) "

    ---> Nel primo caso ottengo un vettore monodimensionale con tre elementi che corrispondono alle tre colonne.
    Nel secondo caso v2 (se è dichiarata Variant) diventa un array di Variant a due dimensioni, in cui la prima indica la riga e la seconda la colonna.
    Per cui è come fosse dimensionata così: Dim v2(1 To 1, 1 To 3). Riferirsi a v2 con indici numerici fuori dagli intervalli produce errore.
    Conclusione: sì, è corretto





  • di Luca73 data: 12/07/2017 19:55:34

    OK anche nelle spigazioni sei piu preciso...
    grazie mille ora capisco cosa intendevi per "appiattire"
    Ciao
    Luca





  • di Mik data: 17/07/2017 15:45:25

    Ciao VecchioFrac,
    sono riuscito a far girare in modo completo la macro!
    Il problema che avevo nella crezione della mail sono riuscito a risolverlo.
    Quindi ti ringrazio ancora una volta per l'aiuto e copio qui di seguito la macro funzionanante nel caso servisse a qualcuno.

     
    Option Explicit
    
    Sub PromemoriaInvioFatture_VF()
    Dim dict As Object
    Dim v As Variant
    Dim cel As Range
    Dim s As String, p As String
    
        Set dict = CreateObject("Scripting.Dictionary")
        
        For Each cel In Range("A1").CurrentRegion.Rows
            If cel.Row > 1 Then
                s = CStr(cel.Cells(1))
                v = Range(Cells(cel.Row, "B"), Cells(cel.Row, "F"))
                v = Application.Transpose(Application.Transpose(v))
                If Not dict.exists(CStr(s)) Then
                    dict.Add CStr(s), Join(v, "|")
                    p = ""
                Else
                    p = dict(s) & "^|" & Join(v, "|") & "^|"
                    dict(s) = Left(p, Len(p) - 2)
                End If
            End If
        Next
        
        For Each v In dict
            s = compose_mail(dict(v))
        Next
    End Sub
    
    
    Private Function compose_mail(s As String)
    Dim v As Variant, vv As Variant, k As Variant
    Dim bodymsg As String
    Dim oggetto As String
    Dim Destinatario As String
    Dim percorsofile As String
    
        v = Split(s, "^|")
        If UBound(v) > 0 Then   'più fatture
            bodymsg = "Gentile Cliente,"con la presente siamo a ricordarLe che oggi sono in scadenza le fatture:""
            For Each vv In v
                k = Split(vv, "|")
                bodymsg = bodymsg & "- Nr. " & k(1) & " del " & k(3) & " per € " & k(2) & ";""
                Destinatario = k(4)
            Next
        Else
            vv = Split(s, "|")
            bodymsg = "Gentile Cliente,"con la presente siamo a ricordarLe che oggi è in scadenza la fattura nr. " & _
                      vv(1) & " del " & vv(3) & " per € " & vv(2) & ";""
            Destinatario = vv(4)
        End If
        
        bodymsg = bodymsg & ""Cordiali saluti."
        bodymsg = Replace(bodymsg, """, vbNewLine)
        
        oggetto = "Promemoria scadenza fatture"
    
        percorsofile = ""
    
        If Destinatario <> "" Then
    
            Shell "C:Program Files (x86)Mozilla Thunderbird	hunderbird -compose " _
            & Chr$(34) & "to='" & Destinatario & "',subject='" & oggetto & "',body='" & bodymsg _
            & "',attachment='" & percorsofile & "'" & Chr$(34), vbNormalFocus
            Application.Wait Now + TimeValue("00:00:03")
            SendKeys "^{ENTER}"
                   
        End If
    
    End Function
    
    
    



  • di Vecchio Frac data: 17/07/2017 19:16:57

    Ottimo!! Grazie del feedback positivo

    Nota per gli eventuali lettori futuri: per un errore di tipografia probabilmente dovuto al parser del forum, nel codice postato da Mik nella Function compose_mail compare l'istruzione:
    bodymsg = "Gentile Cliente,"con la presente siamo a ricordarLe che oggi sono in scadenza le fatture:""
    ci sono due virgolette fuori posto: vanno sostituite con un segno di paragrafo """ (o qualsiasi altro carattere strano va bene!) che poi in fase di resa nel messaggio HTML viene sostituito da replace in un carattere di new line:
    bodymsg = Replace(bodymsg, """, vbNewLine)


    @Mik se ritieni conclusa la discussione puoi cliccare su "Risolta".