Gestione delle ore lavorate, un rapportino giornaliero in Excel

Da diversi anni ormai, lavoro remotamente come freelance. Ad ogni Cliente per il quale ho lavorato e lavorerò,  consegno, congiuntamente alla fattura, un documento nel quale vengono dettagliate le ore impiegate durante lo sviluppo del progetto assegnatomi. E' utile sia a me, per la valutazione del lavoro svolto e delle relative performances, sia al Cliente, che mantiene in questo modo traccia degli sviluppi e dei relativi costi.

In questo post illustrerò come realizzarlo in Excel (attualmente utilizzo la versione 2010), con il metodo MVP (Minimum Viable Product). Per tale motivo mi si vorrà perdonare la scarsa cura per l'estetica, solitamente utilizzo Excel per realizzare dei prototipi da convertire poi in applicazioni, utilizzando DotNet.

Struttura del documento

Sarà composto da tre cartelle: la prima per il raggruppamento dei parametri di configurazione (anno e mese di riferimento, costo orario etc), la seconda per l'inserimento delle ore lavorate, la terza, infine, per l'elaborazione e la stampa del rapportino da consegnare.

Cartella di configurazione (CONFIGURAZIONE)

Dopo avere creato la cartella, inserire, come da immagine sottostante, i valori nelle celle indicate. Colori e caratteri possono, ovviamente, essere definiti a piacere dell'utente. Al fine del corretto funzionamento, è essenziale che i valori vengano inseriti nelle celle corrette, ad esempio, il mese di riferimento nella cella B3 del foglio.

Cartella di inserimento (Mensile)

Per i miei scopi, per ogni giorno lavorato, necessitavano al massimo cinque righe da dedicare ai vari task. Raramente ho avuto necessità di inserirne di più. Qualora fosse necessario averne in numero superiore, occorrerà modificare conseguentemente anche le macro cui fanno riferimento.

 

Si uniscano le celle da A a G relative alla riga 1 e si inserisca la seguente formula:

=CONCATENA("Rapporto mensile lavorativo relativo a ";MAIUSC.INIZ(TESTO(CONFIGURAZIONE!$B$3;"mmmm"));" ";CONFIGURAZIONE!$B$4)

Nel dettaglio, la funzione CONCATENA unisce una serie di stringhe, separate dal punto e virgola. MAIUSC.INIZ trasforma una stringa nel cosiddetto Camel Case, ovvero con il primo carattere in maiuscolo. La funzione TESTO(CONFIGURAZIONE!$B$3;"mmmm"), invece, trasforma il numero del mese nel corrispondente nome. Il risultato sarà un'intestazione variabile, in dipendenza dai valori configurati a sistema.

A questo punto occorre creare la struttura per l'inserimento delle descrizioni dei task e per il calcolo dei relativi importi, per ogni giorno saranno presenti sei righe, di cui cinque adibite all'inserimento ed una al totale del giorno. Creare una cella unione per le prime due colonne, raggruppando sei celle per ogni colonna ed inserire nella cella della colonna Giorno, il valore 1.

Ora occorre che, nella colonna Giorno Settimana relativa ad ogni gruppo, venga visualizzato il nome del corrispondente giorno della settimana. Questo può essere realizzato con la seguente formula:

=SE(A4="";"";SCEGLI(GIORNO.SETTIMANA(DATA(CONFIGURAZIONE!$B$4;CONFIGURAZIONE!$B$3;A4));"DOM";"LUN";"MAR";"MER";"GIO";"VEN";"SAB"))

In questo caso andremo ad utilizzare la funzione SCEGLI che consentirà, in base all'indice restituito da GIORNO.SETTIMANA, di visualizzare il nome corretto. Si noti anche che il primo indice corrisponde alla domenica e non al lunedì. La funzione DATA calcolerà la data del giorno basandosi sul mese ed anno reperiti dal foglio CONFIGURAZIONE che sul numero del giorno prelevato dalla colonna precedente. Per evitare errori di visualizzazione è stata utilizzata la funzione SE che effettua un controllo preventivo sul valore contenuto nella colonna dei giorno, evitando di valutare la formula successiva se la cella non contiene un valore. Questo risulterà utile per i giorni dal 29 al 31, considerando che ogni mese ha un numero proprio di giornate.

Adesso è il momento del calcolo degli importi, per ogni riga, nella colonna corretta, andremo ad inserire le due funzioni che calcoleranno l'importo orario e quello chilometrico relativo ad eventuali trasferte:

=D4*CONFIGURAZIONE!$B$9 (per l'importo orario)

=F4*CONFIGURAZIONE!$B$8 (per l'importo chilometrico)

Serve anche un totale complessivo per ogni singolo importo nel corso della giornata, lo otterremo utilizzando la funzione SOMMA, come segue:

=SOMMA(E4:E8) (nella cella D9, per il numero di ore lavorate)

=SOMMA(E4:E8) (nella cella E9, per l'importo orario)

=SOMMA(F4:F8) (nella cella F9, per i chilometri percorsi complessivamente)

=SOMMA(G4:G8) (nella cella G9, per l'importo chilometrico complessivo)

Una volta replicato quanto sopra per ogni giorno dal 1 al 31, ci muoveremo verso il basso, andando a modificare il 29, il 30 ed il 31. A tale fine, considerando ad esempio il 29, andremo ad inserire nella prima colonna la seguente formula:

=SE(CONFIGURAZIONE!$B$6>=29;29;"")

replicandola poi, per il 30 ed il 31. L'effetto sarà quello di non vedere popolate le celle, qualora non previste nel mese in corso. Lascio al lettore il compito di formattare le celle in relazione al risultato che vuole ottenere (ad esempio il numero di decimali utilizzati in visualizzazione).

Infine dobbiamo calcolare l'importo complessivo, sia per le ore lavorate, che per i chilometri percorsi. Lo faremo con la funzione seguente:

Public Function intCalculateTotal(intColumnIndex As Integer)
        
    Dim intCounter As Integer
    Dim dblTotal As Double
    Dim dblPartial As Double

    Application.Volatile True ' avvia il ricalcolo quando una qualsiasi cella di qualsiasi foglio viene cambiata
        
    For intCounter = 9 To 189
        dblPartial = Sheets("Mensile").Cells(intCounter, intColumnIndex)
        If (intCounter - 9) Mod 6 = 0 Then
            dblTotal = dblTotal + dblPartial
        End If
    Next intCounter
    
    intCalculateTotal = dblTotal
    
End Function

che inseriremo nelle celle D192, E192, F192, G192, richiamandola, rispettivamente con gli indici 4,5,6,7 nel seguente modo:

=intCalculateTotal(valore_dell_indice)

L'effetto risultante dovrebbe essere questo:

Cartella riassuntiva (Rapportino)

Ora ci serve produrre e stampare il report da inviare al Cliente, nel report compariranno solo i giorni, le descrizioni delle attività svolte ed il numero di ore impiegate. Si definisca un'intestazione a piacere, utilizzando le formule descritte in precedenza, avendo cura di lasciare libere le righe successive alla quattro (compresa), nelle quali verrà inserito quanto sopra.

Creiamo quindi una funzione che consenta il reperimento delle informazioni da stampare, prelevandole dal foglio Mensile

Public Sub createReport()

    Dim rng As Range
    Dim intCurrentRow As Integer
   
    Set rng = Sheets("Mensile").Range("A4:A188")
    intCurrentRow = 4
    
    Application.ScreenUpdating = False
    Application.DisplayAlerts = False
    
    For Each cell In rng
        '
        ' copia delle sole celle non del totale
        '
        If (cell.Row - 3) Mod 6 <> 0 Then
            If Len(Sheets("Mensile").Cells(cell.Row, 3).Value) > 0 Then
                m_copyRow cell.Row, intCurrentRow
                intCurrentRow = intCurrentRow + 1
            End If
        End If
       
    Next
           
    Application.ScreenUpdating = True
    Application.CutCopyMode = False
    Application.DisplayAlerts = True
    
End Sub


Sub m_copyRow(intSourceRow As Integer, intDestinationRow As Integer)
    
    '
    ' recupera la data, solo se non è la prima riga e la prima è vuota
    '
    Dim intFirstDay As Integer
    Dim intFirstDayRow As Integer
    Dim strDate As String
    
    intFirstDayRow = intSourceRow - ((intSourceRow - 4) Mod 6) ' 4 = prima riga di partenza, 6 = numero righe per giorno
        
    If m_intLastDayRow <> intFirstDayRow Then
        '
        ' copia della riga con data
        '
        Sheets("Mensile").Range("A" & intFirstDayRow & ":B" & intFirstDayRow).Copy Sheets("Rapportino").Range("A" & intDestinationRow & ":B" & intDestinationRow)
        Sheets("Mensile").Range("C" & intSourceRow & ":D" & intSourceRow).Copy Sheets("Rapportino").Range("C" & intDestinationRow & ":D" & intDestinationRow)
        m_intLastDayRow = intFirstDayRow
        
    Else
        '
        ' esegue la copia della riga intera
        '
        Sheets("Mensile").Range("A" & intSourceRow & ":D" & intSourceRow).Copy Sheets("Rapportino").Range("A" & intDestinationRow & ":D" & intDestinationRow)
    
    End If
            
End Sub

 

Ed assegniamo la funzione CreateReport ad un tasto, come da immagine. Per farlo, selezioniamo il tasto dal tab Sviluppo/Inserisci quindi vi clicchiamo sopra con il tasto destro del mouse. A questo punto Assegna macro, selezioniamo la nostra macro ed il gioco è fatto.

Questo è tutto, spero sia stato utile, nel caso preferiate disporre del file originale, potete scaricarlo agendo sul tasto che precede la sezione dei commenti. Il prossimo post sarà relativo alla realizzazione di un foglio Excel per la gestione dei task, con un occhio alla misurazione della produttività personale.

A presto!

Add comment