# Datenfluss

> Wie die DataBridge-Jobs ineinandergreifen — Zeitplan, Filter, Response-Jobs und Löschverarbeitung.

---

LLMS index: [llms.txt](/llms.txt)

---

<div class="pageinfo pageinfo-primary">

Diese Seite richtet sich an die IT-Administration — vertiefende technische Referenz mit
Job-Filtersyntax und Zeitplänen.

</div>


Diese Seite zeigt, *wie* die Daten fließen. *Welche* Felder dabei zugeordnet werden,
steht auf den [Mapping-Detailseiten](../).

## Grundprinzip

Jeder Job der DataBridge ist ein eigenständiger, zeitgesteuerter Lauf:

```mermaid
flowchart LR
  A["Zeitplan<br/><code>Schedule</code>"] --> B["Quelle lesen<br/><code>SourceConnection</code>"]
  B --> C["Filter anwenden<br/><code>Filter</code> + <code>%watermark%</code>"]
  C --> D["Mapping ausführen<br/>Feldzuordnung + Transformation"]
  D --> E["Key-Auflösung<br/>Datensatz im Ziel finden"]
  E --> F["Schreiben<br/><code>TargetConnection</code>"]
  F --> G["Watermark fortschreiben<br/><code>WatermarkField</code>"]
  G -.->|falls gesetzt| H["Response-Job starten<br/><code>ResponseJob</code>"]
```

Die Reihenfolge innerhalb eines Laufs steuert das Feld `Order` (110 – 760).
Stammdaten laufen zuerst (110 – 190), damit die Lookups der späteren Jobs
(Firma, Angebot, Abo) ihre Referenzdatensätze bereits vorfinden.

Die meisten Jobs laufen **alle 2 Minuten** (`0 */2 * * * *`), die Jobs rund um
Verkaufschancen und Angebotsaktivierung **alle 3 Minuten** (`0 */3 * * * *`).

## Gesamtfluss

```mermaid
flowchart TB
  subgraph S1["1 · Stammdaten &nbsp;(Order 110–190)"]
    direction LR
    ST["BC Referenztabellen<br/>Länder · Sprachen · Anreden<br/>Verkäufer · Positionen<br/>Rabattgruppen"] --> STC["CE Wertelisten<br/><code>wysa_country</code>, <code>wysa_language</code>,<br/><code>wysa_bcsalutation</code>, <code>wysa_salesperson</code>, …"]
    IT["BC Verkaufschancen-Elemente<br/><code>acdopportunityelements</code>"] --> ITC["CE <code>product</code>"]
  end

  subgraph S2["2 · Geschäftspartner &nbsp;(Order 220–241, 410–440, 520–530)"]
    direction LR
    CEA["CE <code>account</code> / <code>contact</code>"] -->|"neu: <code>CEAccounts</code>, <code>CEContacts</code>"| BCA["BC <code>acdcontactscomp</code> / <code>acdcontacts</code>"]
    CEA -->|"geändert: <code>CEAccountUpdates</code>, <code>CEContactsUpdates</code>"| BCA
    BCA -->|"Response: Nummer zurück"| CEA
    BCA -->|"<code>BCAccounts</code>, <code>BCContacts</code>"| CEA
  end

  subgraph S3["3 · Verkaufschancen &nbsp;(Order 260–261, 710–760)"]
    direction LR
    CEO["CE <code>opportunity</code><br/><code>opportunityproduct</code>"] -->|"<code>CEOpportunities</code><br/><code>CEOpportunityProducts</code>"| BCO["BC <code>acdopportunities</code><br/><code>acdopportunitycalculationelements</code>"]
    BCO -->|"Response"| CEO
  end

  subgraph S4["4 · Angebote &amp; Abos &nbsp;(Order 550–625)"]
    direction LR
    BCQ["BC <code>acdsalesheaders</code><br/><code>acdsalescalculationelements</code><br/><code>serviceObjects</code> / <code>serviceCommitments</code>"] --> CEQ["CE <code>quote</code> / <code>quotedetail</code><br/><code>wysa_bcserviceobject(commitment)</code>"]
  end

  subgraph S5["5 · Löschungen &nbsp;(Order 420–690)"]
    direction LR
    LOG["BC Änderungsprotokoll<br/><code>acdlogentries</code>"] --> DEL["CE: Datensatz löschen<br/><code>account</code>, <code>contact</code>, <code>product</code>,<br/><code>quote</code> deaktivieren/schließen,<br/><code>quotedetail</code> löschen"]
  end

  S1 --> S2 --> S3 --> S4 --> S5
```

## Muster 1 — Referenzdaten BC → CE

Der einfachste Fall: BC ist führendes System, CE liest nur mit.

```mermaid
sequenceDiagram
  participant BC as Business Central
  participant DB as DataBridge
  participant CE as Customer Engagement
  DB->>BC: GET api page<br/>?$filter=lastModifiedDateTime gt %watermark%
  BC-->>DB: geänderte Datensätze
  DB->>DB: Mapping + Lookup auf CE-Key
  DB->>CE: Upsert (anlegen oder aktualisieren)
  DB->>DB: Watermark = höchstes lastModifiedDateTime
```

Die Abgrenzung erfolgt über das **Watermark**: der Job merkt sich den höchsten
Wert von `WatermarkField` (`lastModifiedDateTime`, Typ `datetime`) des letzten
Laufs und liest beim nächsten Mal nur, was seitdem geändert wurde.

Betroffene Mappings: alle `BC*`-Mappings außer den Delete- und Response-Varianten.

Die Jobs in Gegenrichtung (`CE*`-Mappings) brauchen kein Watermark-Feld: Für
Dataverse-Quellen nutzt die DataBridge das **Change Tracking** der Plattform und erhält
damit nur die seit dem letzten Lauf geänderten Datensätze.

## Muster 2 — CE → BC mit Response-Job

Legt der Vertrieb in CE eine Firma, einen Kontakt oder eine Verkaufschance an,
existiert diese in BC noch nicht. BC vergibt beim Anlegen die Nummer und die
SystemId — diese müssen zurück nach CE. Genau dafür gibt es den **Response-Job**:

```mermaid
sequenceDiagram
  participant CE as Customer Engagement
  participant DB as DataBridge
  participant BC as Business Central

  Note over DB: Job "accountsnewfromce" (Order 220)
  DB->>CE: lies account
  Note right of DB: Filter:<br/>wysa_bcsystemid IS NULL<br/>UND wysa_sync2bc = "Freigegeben"
  CE-->>DB: neue, freigegebene Firmen
  DB->>BC: POST acdcontactscomp
  BC-->>DB: number, id (SystemId)

  Note over DB: ResponseJob "accountsresponsefrombc" (Order 221)
  DB->>BC: lies acdcontactscomp
  BC-->>DB: number, id
  DB->>CE: schreibe wysa_bccontactnumber, wysa_bcsystemid
  Note right of CE: Ab jetzt greift<br/>"accountupdatesfromce" (Order 520)
```

Der entscheidende Mechanismus ist das Feld **`wysa_bcsystemid`**:

* **leer** → der Datensatz ist BC noch unbekannt → der *Neu*-Job greift
  (`CEAccounts`, `CEContacts`, `CEOpportunities`, `CEOpportunityProducts`)
* **gefüllt** → der Datensatz existiert in BC → der *Update*-Job greift
  (`CEAccountUpdates`, `CEContactsUpdates`, `CEOpportunityProductsUpdates`)

Zusätzlich muss `wysa_sync2bc` auf „Freigegeben" stehen — siehe
[Freigabe nach Business Central](../../business-logik/sync-nach-bc/).

Response-Jobs sind bewusst mit `IsActive = false` konfiguriert. Sie werden **nicht**
über den eigenen Zeitplan gestartet, sondern ausschließlich vom vorgelagerten Job
über dessen Feld `ResponseJob` aufgerufen.

| Auslösender Job | Order | Response-Job | Order |
|---|---|---|---|
| `accountsnewfromce` | 220 | `accountsresponsefrombc` | 221 |
| `contactsnewfromce` | 240 | `contactsresponsefrombc` | 241 |
| `opportunitiesnewfromce` | 260 | `opportunitiesresponsefrombc` | 261 |
| `opportunityproductsnewfromce` | 710 | `opportunityproductsresponsefrombc` | 760 |

## Muster 3 — Löschungen über das Änderungsprotokoll

Ein gelöschter Datensatz taucht in der normalen API Page nicht mehr auf — er kann
also nicht per Abgleich erkannt werden. Stattdessen liest die DataBridge das
BC-Änderungsprotokoll `acdlogentries`:

```mermaid
flowchart LR
  DEL["Löschung in BC"] --> LOG["Eintrag in<br/><code>acdlogentries</code><br/>tableId + operation"]
  LOG --> F{"Filter je Job"}
  F -->|"tableId 5050 (Kontakt)"| A["<code>BCAccountsDelete</code><br/><code>BCContactsDelete</code>"]
  F -->|"tableId 72077781 (Artikel)"| P["<code>BCProductsDelete</code>"]
  F -->|"tableId 36 (Angebot)"| Q["<code>BCSalesQuotesDelete</code>"]
  F -->|"tableId 72077783 (Kalkulation)"| K["<code>BCSalesCalculationElementsDelete</code>"]
  A --> X["Mapping setzt<br/><code>statecode</code> / <code>statuscode</code><br/>→ Datensatz wird <b>deaktiviert</b>"]
  P --> X
  Q --> Y["Job-Feld <code>Action = Delete</code><br/>→ Datensatz wird <b>gelöscht</b>"]
  K --> Y
```

Diese Jobs unterscheiden sich in zwei Punkten von den übrigen:

* Die **Quelle** ist immer `acdlogentries`, nie die Fachtabelle. Der Filter grenzt
  über `tableId` die betroffene BC-Tabelle ab, z. B.
  `{{lastModifiedDateTime gt %watermark%}} and tableId eq 5050` für Kontakte/Firmen.
  Als Schlüssel dient `recordId` → `wysa_bcsystemid`.
* Es gibt **zwei Varianten**, wie im Ziel reagiert wird:

| Variante | Jobs | Verhalten in CE |
|---|---|---|
| **Deaktivieren** (kein `Action`-Feld) | `accountsfrombcdelete`, `contactsfrombcdelete`, `productsfrombcdelete` | Das Mapping setzt per `DefaultValue` den `statecode` auf `1` und den `statuscode` auf einen Inaktiv-Wert — der Datensatz bleibt erhalten, wird aber inaktiv gesetzt. Firma und Kontakt verwenden den lösungseigenen Statusgrund `799840001`, das Produkt den Dataverse-Standardwert `2` |
| **Schließen** (kein `Action`-Feld) | `salesquotesfrombcdelete` | Das Mapping setzt `statecode = 3` (Geschlossen) und `statuscode = 6` (Storniert) — das Angebot bleibt erhalten |
| **Löschen** (`Action = Delete`) | `salescalculationelementsfrombcdelete` | Die DataBridge löscht den Datensatz im Ziel |

Stammdaten wie Firma, Kontakt und Produkt werden also **nie hart gelöscht** — für
Historie und referenzielle Integrität in CE ist das wichtig. Angebote werden storniert,
nur Angebotspositionen werden entfernt.

## Sonderfall — Angebot aktivieren

`BCSalesQuotesActivate` (Order 625) ist kein Datenabgleich, sondern eine
Statusänderung. Der Job liest ebenfalls `acdlogentries`, filtert aber auf
`tableId eq 36 and operation eq 'TRANSFERRED'` — also auf den Moment, in dem in
BC aus einem Angebot ein Auftrag wird — und setzt daraufhin den Status des
zugehörigen `quote` in CE.

## Ausführungsreihenfolge und Takt

Die Jobs tragen eine **Ordnungsnummer** (`Order`), die festlegt, in welcher Folge sie
innerhalb eines Durchlaufs abgearbeitet werden. Darauf beruhen die
Reihenfolgeabhängigkeiten der Integration: Stammdaten vor Belegen, Firma vor Kontakt,
Kontakt vor Verkaufschance.

| Order | Job | Bereich | Takt |
|---|---|---|---|
| 110 | `countriesfrombc` | [Stammdaten](../stammdaten/wertelisten/) — Länder | 2 Min |
| 115 | `languagesfrombc` | [Stammdaten](../stammdaten/wertelisten/) — Sprachen | 2 Min |
| 120 | `salutationsfrombc` | [Stammdaten](../stammdaten/wertelisten/) — Anreden | 2 Min |
| 125 | `customerdiscountgroupsfrombc` | [Stammdaten](../stammdaten/wertelisten/) — Rabattgruppen | 2 Min |
| 130 | `salespersonsfrombc` | [Stammdaten](../stammdaten/bcsalespersons/) — Verkäufer | 2 Min |
| 135 | `positionsfrombc` | [Stammdaten](../stammdaten/wertelisten/) — Organisationsebenen | 2 Min |
| 190 | `opportunityelementsfrombc` | [Verkaufschance](../verkaufschance/bcopportunityelements/) — Kalkulationselemente | 2 Min |
| 220 | `accountsnewfromce` | [Firma](../firma/ceaccounts/) — neu nach BC | 2 Min |
| ↳ 221 | `accountsresponsefrombc` | [Firma](../firma/bcaccountsresponse/) — Rückmeldung | *nach 220* |
| 240 | `contactsnewfromce` | [Kontakt](../kontakt/cecontacts/) — neu nach BC | 2 Min |
| ↳ 241 | `contactsresponsefrombc` | [Kontakt](../kontakt/bccontactsresponse/) — Rückmeldung | *nach 240* |
| 260 | `opportunitiesnewfromce` | [Verkaufschance](../verkaufschance/ceopportunities/) — neu nach BC | 2 Min |
| ↳ 261 | `opportunitiesresponsefrombc` | [Verkaufschance](../verkaufschance/bcopportunitiesresponse/) — Rückmeldung | *nach 260* |
| 410 | `accountsfrombc` | [Firma](../firma/bcaccounts/) — aus BC | 2 Min |
| 420 | `accountsfrombcdelete` | [Firma](../firma/bcaccountsdelete/) — Löschung | 2 Min |
| 430 | `contactsfrombc` | [Kontakt](../kontakt/bccontacts/) — aus BC | 2 Min |
| 440 | `contactsfrombcdelete` | [Kontakt](../kontakt/bccontactsdelete/) — Löschung | 2 Min |
| 450 | `productsfrombcdelete` | [Stammdaten](../stammdaten/bcproductsdelete/) — Produkte deaktivieren | 2 Min |
| 520 | `accountupdatesfromce` | [Firma](../firma/ceaccountupdates/) — Änderungen nach BC | 2 Min |
| 530 | `contactupdatesfromce` | [Kontakt](../kontakt/cecontactsupdates/) — Änderungen nach BC | 2 Min |
| 550 | `serviceobjectsfrombc` | [Abonnement](../abonnement/bcserviceobjects/) — Abonnements | 2 Min |
| 560 | `servicecommitmentsfrombc` | [Abonnement](../abonnement/bcservicecommitments/) — Zeilen | 2 Min |
| 610 | `salesquotesfrombc` | [Angebot](../angebot/bcsalesquotes/) — Angebote aus BC | 2 Min |
| 620 | `salescalculationelementsfrombc` | [Angebot](../angebot/bcsalescalculationelements/) — Positionen aus BC | 2 Min |
| 621 | `salescalculationelementsfrombcdelete` | [Angebot](../angebot/bcsalescalculationelementsdelete/) — Positionen löschen | 2 Min |
| **625** | `activatesalesquotesfrombc` | [Angebot](../angebot/bcsalesquotesactivate/) — gewonnene Angebote | **3 Min** |
| 690 | `salesquotesfrombcdelete` | [Angebot](../angebot/bcsalesquotesdelete/) — Angebote stornieren | 2 Min |
| **710** | `opportunityproductsnewfromce` | [Verkaufschance](../verkaufschance/ceopportunityproducts/) — Positionen neu | **3 Min** |
| **750** | `opportunityproductsupdatesfromce` | [Verkaufschance](../verkaufschance/ceopportunityproductsupdates/) — Positionen ändern | **3 Min** |
| ↳ 760 | `opportunityproductsresponsefrombc` | [Verkaufschance](../verkaufschance/bcopportunityproductsresponse/) — Rückmeldung | *nach 710* |

Mit `↳` gekennzeichnet sind die vier **Response-Jobs**. Sie haben keinen eigenen
Zeitplan, sondern werden unmittelbar von ihrem auslösenden Job gestartet und folgen
damit dessen Takt.

### Die Ordnungsnummer sortiert nur innerhalb eines Takts

Das ist die wichtigste Einschränkung an der Tabelle oben: Es gibt **zwei Zeitpläne**.
23 Jobs laufen alle zwei Minuten, drei alle drei Minuten — und diese drei sind fett
hervorgehoben.

```mermaid
flowchart TB
  subgraph M0["Minute 0, 6, 12, 18 …"]
    A0["<b>alle</b> Jobs<br/>2-Minuten- und 3-Minuten-Gruppe<br/>→ Ordnungsnummer gilt durchgehend"]
  end
  subgraph M2["Minute 2, 4, 8, 10 …"]
    A2["nur die 23 Jobs der<br/><b>2-Minuten-Gruppe</b>"]
  end
  subgraph M3["Minute 3, 9, 15, 21 …"]
    A3["nur die 3 Jobs der<br/><b>3-Minuten-Gruppe</b><br/>625, 710, 750"]
  end
```

Die Ordnungsnummer legt also nur die Folge der Jobs fest, die **im selben Tick**
laufen. Die beiden Gruppen treffen sich alle sechs Minuten.

Praktisch bedeutet das für die drei Jobs im Drei-Minuten-Takt:

| Job | Braucht Vorarbeit von | Konsequenz |
|---|---|---|
| 710 · Positionen neu nach BC | 190 (Kalkulationselemente) und 260 / 261 (Verkaufschance) | läuft in einem eigenen Tick — die Vorarbeit stammt aus einem **früheren** Durchlauf |
| 750 · Positionen ändern | 710, 760 | läuft im selben Tick wie 710, hier gilt die Ordnungsnummer |
| 625 · gewonnene Angebote | 610 (Angebote aus BC) | anderer Takt als 610 |

Weil die Vorgänger-Jobs **häufiger** laufen als die abhängigen, ist die Vorarbeit in der
Praxis immer schon erledigt — nur eben aus einem früheren Durchlauf, nicht aus demselben.
Wer eine Reihenfolgeabhängigkeit prüft, sollte deshalb nicht allein auf die
Ordnungsnummer schauen, sondern auch auf den Takt.

> **Beim Ändern eines Zeitplans beachten**
>
> Würde ein Job aus dem Zwei-Minuten-Takt in den Drei-Minuten-Takt verschoben (oder
> umgekehrt), ändert sich damit auch, ob seine Ordnungsnummer gegenüber anderen Jobs
> überhaupt noch wirkt. Ein Zeitplanwechsel ist also keine reine Performancefrage,
> sondern kann Reihenfolgeabhängigkeiten aufheben.



## Die Operationen im Überblick

Sieben Operationen kommen in den Mappings vor. Sie sind das eigentliche Werkzeug der
DataBridge — alles, was über eine reine Feld-zu-Feld-Zuordnung hinausgeht, ist eine
dieser Operationen.

| Operation | Richtung | Was sie tut | Wo sie vorkommt |
|---|---|---|---|
| `Lookup` | BC → CE | macht aus einem BC-Kürzel eine Dataverse-Verknüpfung | überall in den BC→CE-Mappings |
| `LookupValue` | CE → BC | löst eine Dataverse-Verknüpfung zum BC-Kürzel auf | überall in den CE→BC-Mappings |
| `DefaultValue` | beide | schreibt einen festen Wert ohne Quellfeld | Freigaben, Statuswerte, Kontaktarten, feste GUIDs |
| `ReadFromMessage` | BC → CE | liest einen Wert aus der Antwortnachricht statt aus einer Tabelle | alle vier [Response-Jobs](../firma/bcaccountsresponse/) |
| `OptionMapping` | BC → CE | übersetzt einen BC-Textwert in einen Dataverse-Auswahlwert | nur [`BCServiceCommitments`](../abonnement/bcservicecommitments/) |
| `LinkToRecord` | CE → BC | baut aus der Datensatz-GUID einen aufrufbaren Deep Link | nur [`CEOpportunities`](../verkaufschance/ceopportunities/) |
| `SetNullIfEqualsValueOfField` | BC → CE | Parameter von `Lookup`: leert die Referenz bei Selbstbezug | nur [`BCAccounts`](../firma/bcaccounts/) |

Die letzten drei sind Einzelfälle — jede kommt in genau einem Mapping vor. Wer die
Konfiguration erweitert, findet dort das jeweilige Muster.

## Nächster Schritt

Die Feldzuordnungen der einzelnen Mappings sind in der
[Mapping-Übersicht](../) gelistet. Die Detailseiten sind nach Datenbereich gegliedert:
[Stammdaten](../stammdaten/), [Firma](../firma/), [Kontakt](../kontakt/),
[Verkaufschance](../verkaufschance/), [Angebot](../angebot/) und
[Abonnement](../abonnement/).
