# Kalenderquellen

> Ein Paket zeigt seine Datensätze mit Datum im Kalender von nexxus, neben Feiertagen, Urlaub und den eigenen Terminen der Person. Wie der Abschnitt calendar im Manifest funktioniert, wer welche Einträge sieht und wie das Verschieben eines Eintrags in den Datensatz zurückschreibt.

Viele Datensätze haben ein Datum, nach dem Menschen planen: eine Frist, ein Termin mit einer Patientin, eine Lieferung. Dafür hat nexxus einen [Kalender](/de/use/calendar/) unter <MenuPath to="Start > Kalender" />. Ein Paket baut keinen eigenen. Es deklariert in seinem Manifest **Kalenderquellen**: welche Datensätze welchen Fachobjekts, nach welchen Datumsfeldern, für wen, in welcher Farbe und ob man sie verschieben darf. nexxus liest die Datensätze über die normale Datensatzsuche und schreibt ein Verschieben über die normale Datensatzänderung, also gelten Rechte, Prüfungen und Ereignisse des Pakets wie bei jeder Bearbeitung.

<Flow
  steps={[
    { icon: 'file-json', title: 'Manifest', text: 'Das Paket nennt in calendar Fachobjekt, Datumsfelder, Titel, Personen und Farben.' },
    { icon: 'search', title: 'Datensatzsuche', text: 'nexxus fragt die Datensätze der gezeigten Tage ab, mit den Rechten der lesenden Person.', tone: 'cyan' },
    { icon: 'calendar-days', title: 'Kalender', text: 'Zeigt sie neben Feiertagen, Urlaub und Terminen, nach Meine oder Alle.', tone: 'lavender' },
    { icon: 'move', title: 'Datensatzänderung', text: 'Ziehen schreibt das neue Datum zurück, mit den Prüfungen des Datensatzes und dem Ereignis updated.' },
  ]}
  caption="Das Paket bringt keinen Kalendercode mit. Alles läuft über die Datensatz-API, die es ohnehin hat."
/>

## Der Abschnitt calendar im Manifest

`calendar` ist eine Liste auf oberster Ebene. Jeder Eintrag ist eine Quelle mit einem eigenen Chip im Kalender.

```json
"calendar": [
  {
    "key": "fristen",
    "entity": "frist",
    "label": { "de": "Fristen", "en": "Deadlines" },
    "icon": "event_upcoming",
    "start": "faellig_am",
    "title": "titel",
    "subtitle": ["akte", "typ"],
    "people": ["zustaendig"],
    "colorField": "typ",
    "colors": { "notfrist": "red", "frist": "amber", "vorfrist": "teal" },
    "filter": { "status": "offen" },
    "move": false
  }
]
```

| Feld | Pflicht | Bedeutung |
|---|---|---|
| `key` | ja | Name der Quelle, eindeutig in der Liste. Ein Kleinbuchstabe, dann Kleinbuchstaben, Ziffern, `-` und `_`. |
| `entity` | ja | Ein Fachobjekt des Pakets. |
| `label` | ja | Der Text des Chips, deutsch und englisch, wahlweise auch polnisch. |
| `icon` | nein | Ein Name aus Material Symbols. Vorgabe: das Icon des Fachobjekts. |
| `start` | ja | Ein Feld vom Typ `date` (ganztägiger Eintrag) oder `datetime` (Eintrag mit Uhrzeit), `indexed: true`. |
| `end` | nein | Ein Feld vom selben Typ wie `start`, indiziert. Ohne `end` dauert ein `date`-Eintrag einen Tag und ein `datetime`-Eintrag 30 Minuten. Ein Ende vor dem Beginn zählt als fehlend. |
| `title` | ja | Ein Feld vom Typ `string`, `text`, `enum` oder `ref`. Ein Enum zeigt seine Bezeichnung, ein Verweis den Anzeigewert seines Ziels. Leer: der Titel des Datensatzes. |
| `subtitle` | nein | Felder, die kleiner unter dem Titel stehen, verbunden mit " · ". Jeder Typ außer `json` und `geo`. |
| `people` | nein | `ref`-Felder mit `target: "core:user"`. Sie entscheiden, wann ein Datensatz als eigener gilt, siehe unten. |
| `colorField` | nein | Ein `enum`-Feld, das jeden Eintrag einfärbt. |
| `colors` | nein | Mit `colorField`: Wert-Schlüssel zu einer Farbe. Die erste ist auch die Farbe des Chips der Quelle. |
| `color` | nein | Ohne `colorField`: die Farbe der ganzen Quelle. |
| `filter` | nein | Ein fester Filter auf indizierte Felder: ein Wert oder eine Liste im Sinn von „einer davon“. Datensätze außerhalb zeigt der Kalender nie. |
| `move` | nein | `true` erlaubt Personen mit Schreibrecht, die Einträge zu verschieben. Vorgabe `false`. |

Die Farben sind Tokens, keine Hex-Werte, damit sie im hellen und im dunklen Design passen: `blue`, `teal`, `green`, `lime`, `amber`, `orange`, `red`, `pink`, `purple`, `slate`. Ein Wert ohne Eintrag in `colors` und eine Quelle ohne `color` und ohne `colors` bekommen ein Token, das aus dem Wert oder dem Schlüssel der Quelle abgeleitet wird. Die Ableitung ist stabil: derselbe Schlüssel ergibt auf jedem Server dieselbe Farbe.

Gesetzliche Fristen und andere Daten, die sich nicht aus Versehen verschieben dürfen, bleiben bei `move: false`. Geändert werden sie dann auf der Seite des Datensatzes, wo die Regeln des Pakets gelten.

## Wer was sieht

Eine Quelle erscheint für eine Person, wenn die Installation des Pakets aktiv ist und die Person das Fachobjekt lesen darf (Recht `<modul>.<fachobjekt>.read`). Datensätze haben keine eigenen Rechte: Wer das Fachobjekt lesen darf, sieht alle seine Datensätze, die den `filter` der Quelle erfüllen. Für den Kalender selbst braucht es das Recht `calendar.use`, das jede Rolle mit Mitarbeiterrechten hat.

Die lesende Person wählt zwischen <Ui>Meine</Ui> und <Ui>Alle</Ui>:

- **Meine** behält die Datensätze, bei denen eines der `people`-Felder die Person enthält. Ein Datensatz mit zwei Personenfeldern (etwa zuständig und Vertretung) erscheint bei beiden.
- **Alle** zeigt jeden Datensatz, den die Person lesen darf. Sind im Personenfilter Personen gewählt, behält der Kalender die Datensätze, bei denen ein `people`-Feld eine von ihnen enthält.
- Eine Quelle **ohne `people`** erscheint nur unter Alle ohne Personenfilter, weil keiner ihrer Datensätze jemandem gehört.

Datumswerte: Ein `date` ist ein Tag ohne Zeitzone; das `end` eines ganztägigen Eintrags zählt mit. Ein `datetime` ist ein Zeitpunkt und erscheint in der Zeitzone des Mandanten (Systemeinstellung), ein Termin um 09:30 in Berlin also für alle im Mandanten um 09:30. Ein Verschieben schreibt ihn mit dem Versatz dieser Zone zurück.

nexxus fragt die Datensätze ab, die in den gezeigten Tagen beginnen, und mit `end` auch die, die früher begonnen haben und noch laufen. Pro Quelle und Anfrage liest es höchstens 2000 Datensätze, je 200 auf einmal. Eine dort abgeschnittene Quelle nennt die Seite: <Ui>Es gibt mehr Einträge, als der Kalender auf einmal zeigt. Ein kürzerer Zeitraum oder ein Personenfilter zeigt alle.</Ui> Kann Ihre Quelle so viele Datensätze in einem Monat haben, ergänzen Sie einen `filter` oder `people`.

Ein Klick auf einen Eintrag öffnet die Seite des Datensatzes, `/x/<modul>/<fachobjekt>/<id>`.

## Einträge verschieben

Mit `move: true` ziehen Personen, die das Fachobjekt schreiben dürfen (`<modul>.<fachobjekt>.write`), Einträge auf einen anderen Tag oder eine andere Zeit, und bei Einträgen mit Uhrzeit die Unterkante, um das Ende zu ändern. Der KI-Assistent verschiebt Einträge genauso, nachdem die Person seine Karte bestätigt hat.

nexxus schreibt den Datensatz dann über die normale Änderung:

- Ein Verschieben schreibt `start` und, wenn der Datensatz ein Ende hat, `end` mit derselben Dauer. Eine Längenänderung schreibt nur `end` und braucht ein `end`-Feld.
- Die Prüfungen laufen wie auf der Seite des Datensatzes. Ein `readonly`-Feld kann kein verschobenes Feld sein; die Paketprüfung lehnt diese Kombination ab.
- Der Datensatz behält seine Historie, und der Dienst des Pakets bekommt `<entity>.updated` wie bei jeder Bearbeitung.
- Die Anfrage enthält den Beginn, den die Person gesehen hat. Beginnt der Datensatz inzwischen woanders, antwortet nexxus mit 409 und dem Eintrag, wie er jetzt ist, und der Kalender zeigt diesen Stand mit der Meldung <Ui>Der Eintrag wurde inzwischen geändert, er beginnt jetzt ...</Ui>

Ohne `move: true` oder ohne Schreibrecht lässt sich der Eintrag nicht ziehen, und der Server lehnt ein Verschieben mit 403 ab.

## Was die Paketprüfung verlangt

`validatePackage` und der Upload lehnen ein Paket mit einer Meldung je Befund ab, wenn:

- `entity` nicht existiert, ein genanntes Feld kein Feld davon ist oder ein `key` doppelt vorkommt.
- `start` nicht `date` oder `datetime` ist oder nicht indiziert. `end` einen anderen Typ als `start` hat, nicht indiziert ist oder das `start`-Feld ist.
- `move` `true` ist und `start` oder `end` `readonly` ist.
- `title` nicht `string`, `text`, `enum` oder `ref` ist; ein `subtitle`-Feld `json` oder `geo` ist oder doppelt vorkommt.
- ein `people`-Feld kein Verweis auf `core:user` ist oder doppelt vorkommt.
- `colorField` kein `enum` ist, ein Schlüssel in `colors` keiner seiner Werte ist, `colors` ohne `colorField` kommt oder `color` mit ihm.
- ein `filter`-Feld nicht indiziert ist, keinen Wert hat oder ein Wert nicht zu seinem Typ passt.

Der Abschnitt ist neu in der Plattform. Eine nexxus-Version ohne ihn lehnt das Manifest wegen des unbekannten Felds ab, setzen Sie `platform` also auf einen Bereich, der bei der Version mit dem Kalender beginnt.

## Wie es weitergeht

- [Kalender](/de/use/calendar/): was Menschen mit Ihren Einträgen sehen und tun.
- [Aufbau eines Pakets](/de/modules/structure/#fachobjekte): Feldtypen und `indexed`.
- [Daten und Aktualisierungen](/de/modules/data-and-upgrades/): welche Felder indiziert sind und was eine neue Version ändern darf.