Einfach, effizient und digital: Zeiterfassung bei avocado

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.

Datenfluss vor der Bridge und danach

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.

Dieser digitale Fluss verhindert Ungenauigkeiten, verringert Fehleranfälligkeit und ist sehr effizient, da zum einen die Erfassung im Hintergrund abläuft und Daten nicht erst gesucht oder kopiert werden, sondern sofort verfügbar sind.

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.

[
  {
	"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.

private gatherRequiredRedmineTimeEntries(mappings: Mapping[]): Observable<RedmineTimeEntry[]> {
    // Sammle alle Ids
    return mappings.reduce((all, next) => all.concat(next.bookings), [])
        // Hole jeden RedmineTimeEntry anhand von Id
            .map(id => this.getTimeEntry(id))
        || of([]);
}

Asynchronität mit Observables

Die von Angular standardmäßig verwendete Bibliothek RxJS gestaltet das Arbeiten mit asynchronen Anfragen von unterschiedlichen Schnittstellen sehr angenehm. Daten werden als Streams angesehen und können mit mächtigen Operationen angepasst werden. Statt nur Objekte zurückzugeben kann ein Stream weitergegeben werden dem entweder Daten entnommen werden oder die darin fließenden Daten verändert werden.

// Lade Daten von API
forkJoin([this.setupMappings(), this.setupProjects(), this.setupIssues()])
    .subscribe(() => {
        // Setze korrekte Auswahl
        this.manageProjectIssueSelection();
        
        // Entferne ladeanimation
        this.loading.next(false);
    });

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.

/**
 * 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.

Dialog der sich nach Auswahl von Timeular Eintrag öffnet

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.


“Das Brücken Projekt zwischen Timeular und Redmine erlaubte es mir die Sprache Typescript tiefer kennenzulernen und den gesamten Entwicklungsprozess verfolgen zu können. Es freut mich diese neue Erfahrung machen zu dürfen und ich hoffe, dass auch in der Zukunft die Mitarbeiter bei avocado davon profitieren können.“

Marlon S. (Student Angewandte Informatik, 5. Semester)

Veröffentlicht am 25. Februar 2021