Einfach, effizient und digital: Zeiterfassung bei avocado

Marlon Schlosshauer, 25. Februar 2021
Lesezeit: 7 Minuten

Als IT Dienstleister nutzt avocado natürlich einen digitalen Prozess um aufgewendete Zeiten an Projekten zu erfassen.  Die Applikation Timeular bietet eine Softwarelösung zum Erfassen von Arbeitszeiten an. Zeiten können entweder über Knopfdruck aufgezeichnet oder per Hand eingetragen werden.

Es benötigt jedoch mehr als eine einzelne Erfassung des Mitarbeiters um zu ermitteln welche Zeiten dem Kunden in Rechnung gestellt werden, da mehrere Teammitglieder gleichzeitig für einen Kunden arbeiten können. Zeiten zu bündeln ist also ebenso wichtig.

Die frei verfügbare Software Redmine eignet sich perfekt zum Sammeln von diesen Zeiten. Für jeden Wunsch des Kunden wird intern ein Ticket erstellt. Mitarbeiter können den von ihnen aufgebrachten Aufwand auf diese Tickets buchen. Am Ende des Monats wird dem Kunden die erbrachte Arbeit in Rechnung gestellt.

Zwar beschleunigen Timeular und Redmine den Prozess, aber das Verwenden von beiden allein schöpft noch nicht das volle Potential aus. Das Ziel ist es Einträge von Timeular direkt in Redmine speichern zu können, ohne längere Interaktion des Benutzers. Eine Synchronisierung mit Redmine wird von Timeular momentan nicht unterstützt, kann aber dank der verfügbaren Schnittstellen beider Applikationen leicht entwickelt werden.

/uploads/flow_1_1024x571_ee2d27ca18.png

Gewünschtes Verhalten

Über den Arbeitstag sollen Zeiten mit Timeular erfasst werden. Am Ende des Tages, oder auch am nächsten Tag, soll der Benutzer die Möglichkeit haben über eine Oberfläche seine Timeular Einträge zu sehen. Sobald einer dieser Einträge ausgewählt wird, werden die internen Tickets und Projekte angezeigt. Der Benutzer kann eines davon auswählen und das Programm übernimmt automatisch Text und Zeiten aus dem gewählten Timeular Eintrag. Nach betätigen des Speichern Buttons wird der Eintrag in Redmine gespeichert und kann somit dort weiterverarbeitet werden.

Die Benutzeroberfläche

Nach einmaliger Eingabe von Redmine und Timeular Zugriffsdaten kann der Benutzer seine in Timeular erstellten Zeiteinträge sehen. Per Knopfdruck auf „Buchen" öffnet sich ein Dialog welcher Informationen zu dem Eintrag liefert und die Auswahl von verschiedenen, Redmine spezifischen, Optionen anbietet. Darunter ist Zeit (in Stunden) und ein Kommentar. Diese beiden Informationen sind besonders wichtig, weil sie in Redmine wiederauftauchen.

Zeiteinträge werden gruppiert und in einer Tabelle dem Benutzer präsentiert. Über einen Button kann eine Gruppe ausgewählt und mit Buchungen bestückt werden. Diese Buchungen werden in der Bridge durch die von Redmine erhaltene Identifikationsnummern quittiert. Nach einer Buchung auf eine Gruppe wird diese in der Tabelle farblich ausgegraut, um zu signalisieren das diese Einträge schon im System abgebildet sind.

Lösung im Detail

Die „Bridge“ besteht aus einem Angular Frontend das per REST Schnittstellen mit Timeular und Redmine kommuniziert. Daten, wie z.B. ob ein Eintrag schon gebucht wurde, werden im lokalen Speicher des Browsers abgelegt. Somit wird weder ein Server noch eine Datenbank benötigt.

Copy
[
  {
    "26390548": [
        583,
        151,
        10
    ]
  },
  {
    "25647989": [
        589
    ]
  }
]

Die Hauptkomponente der Bridge ist der BridgeService, der Inhalte von den anderen Services zusammenführt und als Abstraktion zu dem Erfassungswerkzeug Timeular und Buchungswerkzeug Redmine agiert.

Um eine komplette Buchung darzustellen muss also der Key als Id eines Timeular Eintrag und das angehangene Array als Liste von Redmine Ids interpretiert werden.

Möchte man somit alle Redmine Einträge eines Mappings erhalten kann man dies schon in wenigen Zeilen implementieren.

Copy
    return mappings.reduce((all, next) => all.concat(next.bookings), [])
        // Hole jeden RedmineTimeEntry anhand von Id
            .map(id => this.getTimeEntry(id))
        || of([]);
}

So können Daten nicht nur als Stream bearbeitet werden, sondern es kann auch das Verhalten zurückgehalten werden, bis alle beantragten Informationen wirklich vorhanden sind.

Copy
/**
 * Gibt momentan verwendete Aktivitäten und schon archivierte Aktivitäten zurück
 * @param token Timeular Token zur Authentifizierung
 */
public getAllActivities(token: string): Observable<TimeularActivity[]> {
    return forkJoin([this.getActivities(token), this.getArchivedActivities(token)])
        .pipe(
            map(({[0]: act, [1]:arv}) => act.concat(arv)));
}

Persistenz ohne Server

Erstellte Verknüpfungen zwischen Timeular und Redmine Einträgen sollen sitzungsübergreifend verfügbar sein. Zur Speicherung bieten sich die von Timeular und Redmine intern verwendeten IDs an. Die verknüpften IDs werden in dem Lokalen Speicher des Browsers hinterlegt.

Das Mapping kann somit sehr simple abgebildet werden. Wobei Key die Timeular und Value die Redmine Id ist. So werden auch die Einträge im Lokalen Speicher des Browsers festgehalten.

/uploads/beispiel_dialog_1024x487_7d30ba083f.png

Der zu erstellende Redmine Eintrag kann per „Buchen“ bestätigt werden und wird zwischengespeichert, damit der Nutzer weitere Buchungen erstellen kann, ohne sofort den Dialog verlassen zu müssen.

Wenn alle Buchungen eingetragen wurden kann über „Speichern“ das Schreiben zu Redmine ausgeführt werden. Der Dialog schließt sich und der zuvor ausgewählte Timeular Eintrag wird nun farblich dargestellt, je nachdem wie viel noch nicht gebuchte Zeit sich darauf befindet.