# D365BC — Business Central

> Konnektor für Microsoft Dynamics 365 Business Central. Unterstützt Lesen und Schreiben über OData v4.

---

Site [llms.txt](/llms.txt)

---

Der D365BC-Konnektor verbindet die DataBridge mit **Microsoft Dynamics 365 Business Central**
über die OData-v4-REST-API (API-Version 2.0). Er unterstützt sowohl Cloud-Umgebungen
(OAuth2) als auch OnPremise-Installationen (Basic Auth).

| Eigenschaft | Wert |
|---|---|
| Lesen | ✅ unterstützt |
| Schreiben | ✅ unterstützt |
| Job-Filter | ✅ wird als OData-`$filter` an BC übergeben |
| Status | Freigegeben |
| Getestete Mindestversion | Business Central v17.0 (Oktober 2020) |

> **Die Mindestversion wird nicht geprüft**
>
> Die Angabe zur Mindestversion beschreibt den getesteten Stand. Die DataBridge fragt die
> Version von Business Central **nicht** ab und lehnt ältere Stände nicht ab. Ob eine ältere
> Installation funktioniert, hängt allein davon ab, ob sie die verwendete API-Version 2.0
> und die im Mapping genannten Felder bereitstellt.



---

## Verbindungsparameter

### Cloud-Umgebung (empfohlen)

```json
{
  "clientId": "<App-Registration Client ID>",
  "clientSecret": "<App-Registration Client Secret>",
  "tenantId": "<Entra Tenant ID>",
  "environment": "<BC-Umgebungsname, z. B. production>",
  "companyId": "<Company ID (GUID)>"
}
```

### Cloud-Umgebung mit Managed Identity (ab v2.3)

Ohne Client Secret, über die Managed Identity der Function App:

```json
{
  "authmethod": "ManagedIdentity",
  "clientId": "<Client-ID der user-assigned Identity, optional>",
  "environment": "<BC-Umgebungsname, z. B. production>",
  "companyId": "<Company ID (GUID)>"
}
```

- Ohne `clientId` wird die **system-zugewiesene** Identity der Function App verwendet, mit `clientId` die **benutzer-zugewiesene**.
- `clientSecret` und `tenantId` sind nicht nötig. Der Token wird mit dem `scope` der Connection angefordert und rechtzeitig vor Ablauf erneuert.
- Managed Identity wird **nur** über `authmethod` aktiviert, nie automatisch.
- Voraussetzung: Die Identity ist in Business Central unter *Microsoft-Entra-Anwendungen* angelegt, aktiviert und mit Berechtigungssätzen versehen.

### OnPremise-Umgebung

```json
{
  "userName": "<domain\\benutzername>",
  "password": "<Passwort>",
  "url": "https://<server>:<port>/<pfad>",
  "companyId": "<Company ID (GUID)>"
}
```

Ist `url` gesetzt, wird daraus die Basisadresse gebildet (`<url>/api/`) und `environment`
nicht ausgewertet. Ist `url` leer, verwendet der Konnektor den Cloud-Endpunkt und bildet
die Adresse aus `environment`.

Die Auswahl des Authentifizierungsverfahrens erfolgt **automatisch**: Sind `userName` und
`password` gesetzt, wird Basic Auth verwendet, andernfalls OAuth2 Client Credentials aus
`clientId`, `clientSecret` und `tenantId`. Mit `authmethod` lässt sich die Methode ausdrücklich festlegen.

### Optionale Parameter

Gelten für Cloud und OnPremise:

| Parameter | Standardwert | Beschreibung |
|---|---|---|
| `scope` | `https://api.businesscentral.dynamics.com/.default` | OAuth2-Scope beim Token-Abruf |
| `maxPageSize` | `5000` | Maximale Datensätze pro Abruf-Seite |
| `language` | `de-DE` | Kultur für die Interpretation von Zahlen- und Datumsformaten |
| `httpClientTimeoutSeconds` | `100` | Zeitlimit für einzelne HTTP-Aufrufe in Sekunden |
| `keyVaultUrl` | *(leer)* | Key-Vault-Secret-URL; ersetzt sämtliche übrigen Parameter |
| `logCreateRequests` | `false` | Protokolliert die vollständigen Anfragen beim Anlegen |
| `logUpdateRequests` | `false` | Protokolliert die vollständigen Anfragen beim Aktualisieren |
| `logReceivedData` | `false` | Protokolliert die vollständigen Antwortdaten |

Die drei Diagnose-Schalter erzeugen erhebliche Datenmengen und können Geschäftsdaten im
Klartext protokollieren — nur gezielt für die Dauer einer Analyse aktivieren.
Vollständige Beschreibung unter [Verbindungen](../../konfiguration/verbindungen/).

---

## Tabellen- und Feldnamen

### SourceTable / TargetTable

Der Tabellenname leitet sich aus dem API-Pfad der Business-Central-API v2.0 ab —
**ohne** den Company-Teil der URL.

| API-Typ | URL-Struktur | SourceTable |
|---|---|---|
| Standard-API | `.../api/v2.0/companies({id})/customers` | `v2.0/customers` |
| Custom API | `.../api/{publisher}/{product}/{version}/entities` | `{publisher}/{product}/{version}/entities` |

### SourceField / TargetField

Exakter Feldname aus der BC-API-Seite (case-sensitive). Zu finden in der
BC-API-Dokumentation oder über den API-Explorer in Business Central.

---

## Unterstützte TargetKey-Typen

| Typ | Beschreibung |
|---|---|
| `PrimaryKey` | Systemnativer BC-Primärschlüssel. Der Wert wird in Anführungszeichen gesetzt. |
| `Id` | BC-interne ID (GUID). Der Wert wird **ohne** Anführungszeichen eingesetzt. |
| `CustomKey` | Benutzerdefinierter Schlüssel über ein oder mehrere Felder |
| `Filter` | Suche über Feldwert-Kombination |

> **PrimaryKey und Id sind nicht austauschbar**
>
> Der einzige Unterschied in der erzeugten Anfrage ist die Quotierung: `PrimaryKey` setzt
> den Wert in einfache Anführungszeichen, `Id` nicht. Wird der falsche Typ gewählt, entsteht
> eine syntaktisch ungültige OData-Adresse — eine GUID in Anführungszeichen oder ein
> Textschlüssel ohne Anführungszeichen. Business Central quittiert das mit einem
> Syntaxfehler, nicht mit einem leeren Ergebnis.



### Statische Schlüsselwerte (ab v2.1)

In einem `KeyValue` kann statt `attributeName` ein `StaticValue` angegeben werden. Der
Schlüsselteil wird dann nicht aus dem Quelldatensatz gelesen, sondern fest gesetzt.

```json
{
  "KeyType": "CustomKey",
  "KeyName": "wysa_number",
  "KeyValues": [
    { "keyName": "sourceSystem", "StaticValue": "DataBridge",   "rank": 1 },
    { "keyName": "customerNo",   "attributeName": "bcCustomerNo", "rank": 2 }
  ]
}
```

Statische Werte werden für die BC-Metadatentypen `string`, `guid` und `int32` unterstützt.
Bei jedem anderen Typ des Schlüsselfeldes bricht die Verarbeitung mit einem Fehler ab
(`Metadata type … not yet implemented for CreateKeyString`).

> **Quotierung von String-Schlüsseln seit v2.1 korrigiert**
>
> Bis einschließlich v2.0 wurden Schlüsselwerte vom Typ `string` ohne umschließende
> Anführungszeichen in den `$filter` geschrieben. Ein mehrteiliger Schlüssel mit einem
> String-Anteil führte dadurch zu einer ungültigen Filterabfrage gegen BC. Seit v2.1 wird
> korrekt quotiert.



---

## SourceOperations

### LookupValue

Liest einen Wert aus einer anderen BC-API-Tabelle nach.

```json
{
  "Operations": [
    {
      "Name": "LookupValue",
      "Parameters": {
        "LookupType": "Attribute",
        "SourceField": "countryRegionCode",
        "LookupTable": "publisher/product/v1.0/countriesRegions",
        "LookupField": "name",
        "UseCache": true
      }
    }
  ]
}
```

`LookupType` kennt die beiden Werte `Attribute` und `Name`. **Business Central
unterstützt ausschließlich `Attribute`**; der Wert `Name` führt zu einem Abbruch
(`SourceOperation for Lookups`). `Name` ist dem [D365CE-Konnektor](../d365ce/) vorbehalten.

### ReadFromMessage

Liest einen Wert aus der BC-Antwort — **nur in ResponseJobs**.
Das `SourceField` muss einen `_`-Prefix haben.

```json
{
  "Operations": [
    {
      "Name": "ReadFromMessage"
    }
  ]
}
```

---

## Datentypen — Zieltyp und akzeptierte Quelltypen

Beim Schreiben bestimmt der Datentyp des **Zielfeldes** in den BC-Metadaten, welche
Umwandlung angewendet wird. Der Quelltyp stammt aus dem Feldtyp des Quellsystems und
entscheidet, ob die Umwandlung gelingt.

| Zieltyp (BC-Metadaten) | Akzeptierte Quelltypen | Verhalten und Sonderfälle |
|---|---|---|
| `Boolean` | `boolean`, `string` | Leerwert → `null`. Ein nicht als Wahrheitswert lesbarer Text führt zum Abbruch. |
| `Date` | `date`, `datetime`, `string` | Ausgabe im Format `yyyy-MM-dd`. Leerwert → `0001-01-01`. Nicht lesbares Datum → Abbruch. |
| `DateTimeOffset` | `date`, `datetime`, `string` | Ausgabe im Format `yyyy-MM-ddTHH:mm:ss.fffZ`. Leerwert → `0001-01-01T00:00:00.000Z`. |
| `Decimal` | `decimal`, `money`, `double` | Interpretation mit der Kultur aus `language`. Leerwert → `null`. |
| `Decimal` | `string` *(ab v2.1)* | Interpretation **immer** als `en-US` (Punkt als Dezimaltrennzeichen), unabhängig von `language`. |
| `Double` *(ab v2.1)* | `decimal`, `money`, `double` | Interpretation mit der Kultur aus `language`. **`string` wird nicht akzeptiert** und führt zum Abbruch. |
| `Guid` | `guid`, `string`, `entityreference` | Leerwert → `00000000-0000-0000-0000-000000000000`, **nicht** `null`. |
| `Int32` | `int32`, `optionsetvalue`, `string` | Leerwert → `null`. Nicht als Ganzzahl lesbarer Wert → Abbruch. |
| `String` | alle Quelltypen | Wert wird getrimmt. Leerwert → **leerer String**, das Zielfeld wird also geleert. Liegt eine `OptionMapping`-TargetOperation vor, wird stattdessen der zugeordnete Wert geschrieben; ein unbekannter Quellwert führt zum Abbruch. |

Jeder in der Tabelle nicht genannte BC-Zieltyp führt beim Schreiben zu einem Abbruch
(`No Resolver defined for edmPrimitiveTypeKind …`).

> **Leerer String leert das Zielfeld**
>
> Business Central und NAV verhalten sich hier unterschiedlich: Der BC-Konnektor liefert
> für einen leeren Quellwert einen **leeren String** und überschreibt damit den bisherigen
> Inhalt des Zielfeldes. Der [NAV-Konnektor](../d365nav/) liefert stattdessen `null` — das
> Feld bleibt dort unverändert.



---

## Schreibvorgang und Fehlerverhalten

Vor jedem Schreibvorgang sucht der Konnektor den bestehenden Zieldatensatz — je nach
`TargetKey`-Typ über den Schlüssel oder über einen Filter. Wird ein Datensatz gefunden,
erfolgt ein Update, andernfalls eine Neuanlage.

> **Seit v2.1 kein stilles Überspringen mehr**
>
> Bis v2.0 lieferte die Suche nach einem bestehenden Datensatz bei einem unerwarteten
> HTTP-Status (etwa `500` oder `403`) einen Leerwert zurück — für die DataBridge
> ununterscheidbar von einem nicht vorhandenen Datensatz. Die Folge war eine Neuanlage und
> damit Dubletten im Zielsystem.
> 
> Seit v2.1 werden ausschließlich Erfolgsmeldungen und `404` als gültige Antworten
> akzeptiert. Jeder andere Status löst eine Ausnahme aus; die Verarbeitung der Nachricht
> schlägt fehl und wird über die Service-Bus-Wiederholung erneut versucht.



---

## ResponseJob — BC-IDs zurückschreiben

Wenn BC beim Anlegen eines Datensatzes eine neue ID vergibt (z. B. die Kundennummer),
kann diese über einen ResponseJob automatisch ins Quellsystem zurückgeschrieben werden.

**Beispiel: CE-Kontakt nach BC anlegen und BC-ID zurück nach CE schreiben**

Job A (Hauptjob):
```
SourceConnection: ce-produktion
TargetConnection: bc-produktion
Mapping:          ce-contact-to-bc-customer
ResponseJob:      bc-id-back-to-ce
IsActive:         true
```

Job B (ResponseJob):
```
SourceConnection: bc-produktion
TargetConnection: ce-produktion
Mapping:          bc-response-to-ce-contact
IsActive:         false
```

Mapping für Job B — das BC-ID-Feld:

| SourceField | TargetField | SourceOperation |
|---|---|---|
| `_id` | `wysa_bcid` | `ReadFromMessage` |

---

## Änderungserkennung (Watermark)

| Eigenschaft | Wert |
|---|---|
| Empfohlener WatermarkType | `datetime` |
| Typisches WatermarkField | `lastModifiedDateTime` |
| Filterformat | `{{lastModifiedDateTime gt %watermark%}}` |

Business Central liefert für jede Entität ein `lastModifiedDateTime`-Feld,
das beim Speichern automatisch aktualisiert wird.

Der Filterausdruck aus dem Job wird als OData-`$filter` an die BC-Anfrage angehängt.
Die Regeln zur Klammerung und zum Platzhalter `%watermark%` sind unter
[Jobs → Filter-Syntax](../../konfiguration/jobs/) beschrieben.
