Generelles

Die Schnittstelle von QuickPool (QP) basiert komplett auf REST. Zur Kommunikation mit dem Server muss stets ein Token (JWT) mitgeschickt werden, welche den Benutzer eindeutig identifiziert. Es bestehen keine App-Tokens, der Login muss mit den Credentials von existierenden und freigeschalteten Benutzern passieren. Wie bei JWT üblich, wird ebenfalls einmalig ein Reset-Token mitgeliefert, welches nach Ablauf der Gültigkeit des Tokens verwendet wird um ein neues Tokenpaar zu erhalten.

Base URL

Für die Production Umgebung von QuickPool wird stets die folgende Base URL vorgehängt: https://api.quickpool.ch/api/v1

CRUD

Fast alle Models haben zumindest die folgenden REST Endpunkte:

MethodePfadBeschreibung
GET/<model>Gibt einen Index zurück, welcher gefiltert, sortiert, eingegrenzt und paginated ist
GET/<model>/:idGibt genau einen Eintrag zurück, sofern man berechtigt ist
POST/<model>Erstellt einen neuen Eintrag, sofern die Parameter korrekt sind und gibt den kompletten Datensatz zurück
PUT/<model>/:idFührt einen Update auf den Eintrag durch, sofern dieser existiert. Nur Felder, welche übergeben wurden, werden geändert. Gibt den kompletten, aktualisierten Datensatz zurück
DELETE/<model>/:idFührt einen Softdelete (inactive -> true) aus, sofern der Datensatz existiert. Mehrere Deletes auf dem gleichen Datensatz haben keinen negativen Effekt. Gibt den kompletten Datensatz zurück

Wie oben erwähnt wird die Sichtbarkeit durch das Token ermittelt und auch parameterlose Aufrufe auf den Index werden Informationen zurückgeben.

Queries

Jede Index Abfrage kann mit einem optionalen Query-Parameter q versehen werden, welche es erlaubt auf fast allen Feldern zu filtern. Hier ein Beispiel wie man Schichten filtern kann:

{
  "clients": { "id": [1] },
  "status": ["unassigned", "assigned"],
  "tags": { "id": [319] },
  "people": { "id": [5] },
  "inactive": false
}

Zusätzlich können folgende Query Parameter mitgegeben werden

ParameterBeispielFunktion
sort{"start_date":"asc"}Gibt an, nach welcher Spalte sortiert werden soll
limit10Gibt an, wie viele Einträge pro Seite angezeigt werden sollen
page1Gibt an, welche Seite geladen wird, wobei das Limit berücksichtigt wird

Filtern nach Zeiträumen

Bei Schichten und Verfügbarkeiten kann zusätzlich zu den anderen Queries noch ein Zeitraum angegeben werden, welcher ebenfalls berücksichtigt wird. Zu beachten ist, dass die Zeiten in UTC übergeben werden.

ParameterBeispiel
from2022-10-16T22:00:00.000Z
until2022-10-23T21:59:59.999Z

Das Matching funktioniert so, dass alle Einträge geliefert werden, welche auch nur teilweise vom Zeitraum überlappt werden. Das Startdatum muss ausserdem vor dem Enddatum liegen.

Joins

Um Informationen, über angehängte Entitäten zu erhalten, kann ein Join ausgeführt werden. Das wird die entsprechende Entität in der Antwort anhängen. In der Regel ist es so, dass jedes Feld, welches mit _id aufhört (z.B. client_id) verwendet werden kann um zu joinen. Dabei muss der Teil OHNE _id in das joins Array mitgegeben und als Queryparameter angehängt werden. Beispiel:

joins: ["client","people","tags","location","template", "fileupload"]

Anhängen von Entitäten

Sofern eine Entität mit anderen angereichert werden kann, müssen diese nicht nur über ID mitgegeben werden. Es wird ein Objekt erwartet, welches im Optimalfall das ganze Objekt, welches angehängt werden soll enthaltet, oder zumindest die ID drin beinhaltet. Das Object muss JSON encoded sein.

{
...
   tags: "[{\"id\":17,\"name\":\"Design\",\"description\":null,\"inactive\":false,\"account_id\":1,\"created_at\":\"2020-11-11T13:21:54.985Z\",\"updated_at\":\"2021-01-02T20:42:22.296Z\",\"color\":\"#d55858\",\"icon\":\"gamepad\",\"settings\":{\"show_on_scheduler\":false},\"minutes_per_week\":null},{\"id\":319,\"name\":\"40%\",\"description\":\"\",\"inactive\":false,\"account_id\":1,\"created_at\":\"2022-09-07T11:54:30.148Z\",\"updated_at\":\"2022-09-07T11:54:38.701Z\",\"color\":\"#e62565\",\"icon\":\"percent\",\"settings\":{\"show_on_scheduler\":false},\"minutes_per_week\":800},{\"id\":15,\"name\":\"FT\",\"description\":\"\",\"inactive\":false,\"account_id\":1,\"created_at\":\"2020-10-20T17:19:22.514Z\",\"updated_at\":\"2021-01-02T20:38:34.620Z\",\"color\":\"#3de1a2\",\"icon\":\"linux\",\"settings\":{\"show_on_scheduler\":false},\"minutes_per_week\":null}]"
...
}

Login /auth/login

Wie oben erwähnt funktioniert das Login mit Username und Passwort. Daraufhin werden die wichtigen Informationen, wie Tokenpaar und genauere Angaben zur Person zurückgeschickt.

curl -XPOST 'https://api.quickpool.ch/api/v1/auth/login' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'email=<your@email.com>' \
--data-urlencode 'password=<yourPW>'

Das Resultat wird in etwa so aussehen:

{
	"id": 1,
	"email": "<your@email.com",
	"role": "admin",
	"avatar": "",
	"first_name": "Alex",
	"last_name": "Mihov",
	"client_name": null,
	"color": "#FF0000",
	"token": "eyJhbGciOiJIUzI1NiJ9.asdas......",
	"refresh_token": "eyJhbGciOiJIUzI1NiJ9.e234....",
	"account": {
		"id": 1,
		"name": "Quickshift",
		"logo": "https://api.quickpool.ch/api/v1/s3/show/quickshift/public/logos/c040c358-c838-4443-ae40-a6fcd9b44c0b-Ardeo-Logo-Symbol.png",
		"email": "info@ardeo.ch",
		"contact_name": "Ardeo GmbH",
		"phone": "079 612 02 84",
		"channel": "aaaaaaaa-bbbb-bbbb-cccc-f7550b7a1105"
	},
	"person_id": 1,
	"client_id": null,
	"config": {
		"i18n": {
			"de": {
				"app": {},
				"web": {},
				"general": "{}"
			},
			"en": {
				"app": {},
				"web": {},
				"general": {}
			}
		},
		"features": {
			"tags": {
				"app": {},
				"web": {},
				"general": {
					"enabled": true,
					"navigation": "visible",
					"navigation_index": 7
				}
			},
			"user": {
				"app": {},
				"web": {},
				"general": {
					"enabled": true,
					"navigation": "visible",
					"navigation_index": 0
				}
			},
			"groups": {
				"app": {},
				"web": {},
				"general": {
					"enabled": false,
					"navigation": "hidden",
					"navigation_index": 8
				}
			},
			"people": {
				"app": {},
				"web": {},
				"general": {
					"enabled": true,
					"navigation": "visible",
					"navigation_index": 4
				}
			},
			"shifts": {
				"app": {},
				"web": {},
				"general": {
					"enabled": true,
					"navigation": "visible",
					"navigation_index": 2
				}
			},
			"account": {
				"app": {},
				"web": {},
				"general": {
					"enabled": true,
					"navigation": "visible",
					"navigation_index": 10
				}
			},
			"clients": {
				"app": {},
				"web": {},
				"general": {
					"enabled": true,
					"navigation": "visible",
					"navigation_index": 5
				}
			},
			"reports": {
				"app": {},
				"web": {},
				"general": {
					"enabled": false,
					"navigation": "hidden",
					"navigation_index": 2
				}
			},
			"projects": {
				"app": {},
				"web": {},
				"general": {
					"enabled": false,
					"navigation": "hidden",
					"navigation_index": 6
				}
			},
			"skribble": {
				"app": {},
				"web": {},
				"general": {
					"enabled": true,
					"navigation": "hidden",
					"navigation_index": 0
				}
			},
			"dashboard": {
				"app": {},
				"web": {},
				"general": {
					"enabled": true,
					"first_view": true,
					"navigation": "visible",
					"navigation_index": 1
				}
			},
			"documents": {
				"app": {},
				"web": {},
				"general": {
					"enabled": true,
					"navigation": "visible",
					"navigation_index": 9
				}
			},
			"templates": {
				"app": {},
				"web": {},
				"general": {
					"enabled": true,
					"navigation": "visible",
					"navigation_index": 3
				}
			},
			"availabilities": {
				"app": {},
				"web": {},
				"general": {
					"enabled": true,
					"navigation": "visible",
					"navigation_index": 6
				}
			},
			"export_outlook": {
				"app": {},
				"web": {},
				"general": {
					"enabled": true,
					"navigation": "hidden",
					"navigation_index": 20
				}
			},
			"import_outlook": {
				"app": {},
				"web": {},
				"general": {
					"enabled": true,
					"navigation": "hidden",
					"navigation_index": 20
				}
			},
			"weekly-templates": {
				"app": {},
				"web": {},
				"general": {
					"enabled": true,
					"navigation": "visible",
					"navigation_index": 3
				}
			},
			"digital_signature": {
				"app": {},
				"web": {},
				"general": {
					"enabled": true,
					"navigation": "hidden",
					"navigation_index": 20
				}
			},
			"automatic_assignment": {
				"app": {},
				"web": {},
				"general": {
					"config": {
						"max_computation_seconds": 120
					},
					"enabled": true,
					"navigation": "hidden",
					"navigation_index": 20
				}
			}
		},
		"public_config": {
			"default_language": "de",
			"availability_mode": "presence",
			"worktime_signature": "optional",
			"automatically_confirm_shifts": false
		}
	}
}

Refresh /auth/refresh

Sobald das Token abgelaufen ist (default ist 60min) wird die Schnittstelle einen Error werfen, dass man nicht authentifiziert werden kann. In diesem Fall muss das Token erneuert werden. Das passiert einmal mit dem refresh_token welches man beim Login erhaltet. Dieses wird an den Endpunkt geschickt als payload refresh_token. Die Antwort vom Server wird dann wieder ein Tokenpaar enthalten, welches genau gleich wie das ursprüngliche verwendet werden kann. Das Refresh-Token ist einen Monat gültig.

People /people

Eine Person beinhaltet die wichtigen Infos von Personen, welche in QP verwendet werden. Die E-Mail-Adresse der Person ist meistens dieselbe wie diejenige des Users, welcher stets der Person angeknüpft ist, das ist allerdings nicht ein muss. Personen werden erstellt, damit man die Mitarbeiter der Firma darstellen kann. Dabei wird für jeden Mitarbeiter, egal ob Administrator oder nicht, ein neuer Eintrag erstellt.

Model

{
  "id": 1,
  "avatar": "",
  "first_name": "Martin",
  "last_name": "Pfister",
  "email": "martin.pfister@ardeo.ch",
  "date_of_birth": "1941-01-17",
  "gender": "male",
  "mobile": "+41 79 111 22 33",
  "street_name": "Zürcherstrasse",
  "house_number": "87",
  "address_details": "",
  "postal_code": "8000",
  "city": "Zürich",
  "country": "CH",
  "nationality": "CH",
  "residence_permit": null,
  "type_of_salary": "hourly",
  "hourly_rate": 1000.0,
  "salary": null,
  "color": "#FF0000",
  "admin": false,
  "IBAN": "GB33BUKB20201555555555",
  "account_id": 1,
  "user_id": 1,
  "notes": "Martin is a great employee",
  "created_at": "2020-08-31T12:16:59.016Z",
  "updated_at": "2022-09-16T10:34:10.842Z",
  "inactive": false,
  "marital_status": "single",
  "nr_of_kids": 0,
  "religion": "atheist",
  "entrance_date": "2020-08-07",
  "ahv_number": "756.1234.5678.90",
  "bank_name": "Zürcher Kantonal Bank",
  "budget_id": 1,
  "custom_values": {
    "ma_kategorie": "Höhere Berufsbildung (HF)"
  },
  "short_name": "Tinu"
}

Angehängte Entitäten

Den Personen kann folgendes angehängt werden:

ModelBeschreibung
TagTags werden verwendet um Qualifikationen und ähnliches anzuhängen. Diese werden ebenfalls an Kunden und Schichten angehängt damit das Matching stattfinden kann.
ClientClients stellen die Kunden oder generell die Arbeitsbereiche dar, an denen Schichten zugeteilt werden können. Diese werden ebenfalls für das Matching verwendet
BudgetBudgets stellen die Arbeitszeit pro Monat/Woche/Tag welche der Person zugeteilt werden soll dar
FileuploadsEs können mehrere Files einer Person angehängt werden, welche einerseits von QP verwendet werden (Anzeigebild, Vertrag etc.) oder nur zur Datenablage dienen

Clients /clients

Ein Client stellt einen Arbeitsort dar. Es kann eine Privatperson, Firma, eine Abteilung oder eine Maschine im Betrieb sein. Ein Client kann mehrere Standorte haben. Diese werden unten aufgeführt. Jeder Einsatz braucht einen Client und eine Standort, ohne diese kann die Schicht nicht erstellt werden.

Model

{
  "id": 1,
  "name": "Medical Carers",
  "email": "medical@care.ch",
  "logo": "https://i2.wp.com/files.123freevectors.com/wp-content/uploads/new/signs-symbols/021_medical-symbol-free-vector-l.png?w=800&q=95",
  "vat_id": "CHE-123.456.789",
  "color": "#673fb4",
  "notes": "Medical Care Notes",
  "inactive": false,
  "account_id": 1,
  "user_id": 7,
  "created_at": "2020-08-31T12:16:59.243Z",
  "updated_at": "2022-05-25T21:16:00.814Z",
  "properties": {
    "bonus_night": "20%",
    "bonus_expenses": "KM-Entschädigung von 0.70 CHF/km",
    "bonus_sunday_holiday": "",
    "bonus_weekend_holiday": ""
  },
  "short_name": "MC"
}

Angehängte Entitäten

Den Clients kann folgendes angehängt werden:

ModelBeschreibung
TagTags werden verwendet um Qualifikationen und ähnliches anzuhängen. Diese werden ebenfalls an Personen und Schichten angehängt damit das Matching stattfinden kann.
PeoplePeople stellen Personen dar, denen Schichten zugeteilt werden können. Diese werden ebenfalls für das Matching verwendet
FileuploadsEs können mehrere Files einer Person angehängt werden, welche einerseits von QP verwendet werden (Anzeigebild, Vertrag etc.) oder nur zur Datenablage dienen
LocationsEin Client kann mehrere Standorte haben, dabei kann einer als Hauptstandort ausgewiesen werden (siehe unten)

Shifts /shifts

Eine Schicht (oft auch Einsatz) bringt alle vorherig erfassten Stammdaten zusammen und stellt eine Arbeitsperiode dar. Schichten können durch verschiedene Stati progressieren:

StatusBeschreibung
unassignedDie Schicht hat keine zugewiesene Person, oder die zugewiesene Person wurde wieder entfernt
assignedDie Schicht ist zugewiesen aber noch nicht von der Person bestätigt
confirmedDie Schicht wurde von der Person bestätigt. Accounts können so eingestellt werden, dass die Schichten automatisch bestätigt werden.
rejectedDie Schicht wurde von der Person in der App abgelehnt und muss neu zugewiesen werden.
submittedDie Schicht wurde von der Person abgearbeitet und zur Kontrolle markiert
approvedDie Schicht wurde vom Admin bearbeitet und wird so gespeichert. Sie kann per GUI nicht mehr verändert werden.
declinedDie Schicht wurde vom Admin abgelehnt

Model

{
  "id": 325,
  "start_date": "2021-01-13T20:00:00.000Z",
  "end_date": "2021-01-14T06:00:00.000Z",
  "notes": "",
  "inactive": false,
  "nr_of_required_people": 1,
  "file_url": null,
  "public": false,
  "status": "open",
  "account_id": 3,
  "client_id": 9,
  "location_id": 20,
  "user_id": 55,
  "created_at": "2021-01-13T13:58:00.457Z",
  "updated_at": "2021-06-16T10:15:21.399Z",
  "name": "Morgenschicht",
  "is_template": true,
  "properties": null,
  "silent": false,
  "weekly_template_id": null,
  "project_id": null,
  "fileupload_id": null,
  "custom_values": {
  },
  "visible": "visible",
  "people": [
    {
      "id": 53,
      "avatar": "https://api.quickpool.ch/api/v1/s3/show/powerpeople/public/avatars/171be919-3bdd-4d10-a1ea-bf20e02e2249-Rectangle-Copy-3.png",
      "first_name": "Olivia",
      "last_name": "Brown",
      "email": "olivia.brown@powerpeople.ch",
      "date_of_birth": null,
      "gender": "female",
      "mobile": "+41 79 123 45 72",
      "street_name": null,
      "house_number": null,
      "address_details": null,
      "postal_code": null,
      "city": null,
      "country": null,
      "nationality": null,
      "residence_permit": null,
      "type_of_salary": null,
      "hourly_rate": null,
      "salary": null,
      "color": "#ff2eee",
      "admin": false,
      "IBAN": null,
      "account_id": 3,
      "user_id": 60,
      "notes": "",
      "created_at": "2020-11-26T13:08:08.632Z",
      "updated_at": "2021-07-28T14:16:15.446Z",
      "inactive": false,
      "marital_status": null,
      "nr_of_kids": null,
      "religion": null,
      "entrance_date": null,
      "ahv_number": null,
      "bank_name": null,
      "budget_id": null,
      "custom_values": {
      },
      "short_name": ""
    }
  ],
  "client": {
    "id": 9,
    "name": "Kunde C",
    "email": "info@ost.ch",
    "logo": "https://api.quickpool.ch/api/v1/s3/show/powerpeople/public/logos/99c48df0-88f6-4fee-9d12-cc507af26a2b-customer_c.png",
    "vat_id": "",
    "color": "#ed0c72",
    "notes": "",
    "inactive": false,
    "account_id": 3,
    "user_id": 66,
    "created_at": "2020-11-26T13:23:29.149Z",
    "updated_at": "2021-09-07T14:00:30.422Z",
    "properties": {
      "bonus_night": "",
      "bonus_expenses": "",
      "bonus_sunday_holiday": "",
      "bonus_weekend_holiday": ""
    },
    "short_name": ""
  },
  "tags": []
}