dojo: Variation-Client-Manipulation + Bundle-Components ohne stockLimitation

This commit is contained in:
2026-05-01 16:46:59 +00:00
parent 30f45a5889
commit 7768e54294
2 changed files with 82 additions and 0 deletions
+16
View File
@@ -308,3 +308,19 @@ if (!ct.includes('application/json')) throw new Error('Silent-Deny');
```
---
## 20. POST auf `/rest/pim/variations/clients`
**Was:** Auf dem PIM-Bulk-Pfad für Variations-Clients gibt es nur `DELETE` (entfernen) und `PUT` (hinzufügen/upsert). `POST` antwortet mit http 200 + `text/html` empty — klassisches Silent-Deny. Plenty's eigenes UI nutzt POST hier nicht.
**Stattdessen:** PUT für Add (ist idempotent), DELETE für Remove. Siehe DOJO #35.
---
## 21. `clients`-Property im PUT auf `/rest/items/{id}/variations/{vid}` setzen
**Was:** Naheliegend wirkender Versuch, die Mandanten-Liste der Variation per Standard-PUT zu manipulieren: `PUT /rest/items/{id}/variations/{vid}` mit Body `{clients: [{plentyId, ...}]}`. Plenty antwortet **http 200 + JSON** mit der unveränderten Variation — der `clients`-Schlüssel im Body wird stillschweigend ignoriert. Besonders heimtückisch, weil Status + Content-Type alle Silent-Deny-Checks bestehen — der Verify-Read deckt es erst auf.
**Stattdessen:** Den dedizierten Bulk-Pfad `/rest/pim/variations/clients` nutzen (DOJO #35).
---
+66
View File
@@ -992,3 +992,69 @@ if (!ct.includes('application/json')) throw new Error('Plenty Silent-Deny');
**Entdeckt:** 2026-05-01 nach mehrstündiger Recherche. Korrekter Endpoint gefunden via Browser-Network-Capture des Plenty-Backends; Plenty's eigenes UI nutzt genau diesen Bulk-Pfad.
---
## 35. Variation-Client (Mandanten-Sichtbarkeit) Manipulation in Plenty Cloud
**Lektion:** Mandanten-Sichtbarkeit pro Variation wird in Plenty Cloud über Add/Remove aus der `variationClients`-Liste gesteuert — es gibt **kein `isActive`-Flag pro Mandant**, anders als die UI suggeriert. Endpoints folgen dem PIM-Bulk-Pattern wie bei Tags (`/rest/pim/variations/<resource>` mit `[{variationId, plentyId}]`-Body), aber im Gegensatz zu Tags funktioniert hier auch PUT für Add (idempotent / upsert) — kein zweiter Pfad nötig.
**Lese-Pfad** (sub-include auf Item-GET):
```
GET /rest/items/{itemId}?with=variations.variationClients
→ main.variationClients = [{variationId, plentyId, isActive: true}, ...]
Inaktive Mandanten existieren in der Liste NICHT — fehlt = nicht aktiv.
```
**Mandant entfernen / Webshop deaktivieren:**
```
DELETE /rest/pim/variations/clients
Body: [{variationId, plentyId}, ...]
← { affectedRows: N } (JSON, nicht text/html — Silent-Deny-Check!)
```
**Mandant hinzufügen / Webshop aktivieren:**
```
PUT /rest/pim/variations/clients
Body: [{variationId, plentyId}, ...]
← [{variationId, plentyId}, ...] (Echo, JSON)
```
**Verworfene Kandidaten** (alle gemessen via Live-Probe):
- `PUT /rest/items/{id}/variations/{vid}` mit `clients`-Property → http 200 + JSON, aber `clients` wird **ignoriert** (silent no-op, gefährlich).
- `PUT /rest/pim/variations/{vid}` mit `clients`-Property → Silent-Deny (text/html empty).
- `PUT /rest/items/{id}/variations/{vid}/clients/{cid}` → Silent-Deny.
- `POST /rest/pim/variations/clients` → Silent-Deny (text/html empty).
**Pattern:**
```javascript
const path = '/rest/pim/variations/clients';
const body = [{ variationId, plentyId: clientPlentyId }];
const resp = await client.request({
method: present ? 'PUT' : 'DELETE',
url: path, data: body, validateStatus: () => true,
});
const ct = String(resp.headers['content-type'] || '');
if (!ct.includes('application/json')) throw new Error('Silent-Deny');
// Verify per Re-Read von variationClients (Lese-Lag ≤ 2s).
```
**Entdeckt:** 2026-05-01 — Browser-Network-Capture des Plenty-Backends bestätigt, dass Plenty's eigene UI beim Mandanten-Häkchen-Toggle exakt diesen DELETE-Aufruf macht.
---
## 36. `variationBundleComponents`-Sub-Include liefert kein `stockLimitation`
**Lektion:** Beim Lesen von Bundle-Komponenten via `with=variations.variationBundleComponents,variations.stock` liefert Plenty pro Komponente nur `{rowId, componentVariationId, componentItemId, quantity, netStock, physicalStock}` — das `stockLimitation` der Komponenten-Variation ist **nicht** im Sub-Include. Wer es braucht (z.B. um „bestandsabhängig vs. unbegrenzt" pro Komponente zu erkennen), muss entweder pro Komponente einen separaten Item/Variation-Read machen oder eine domänenspezifische Annahme treffen.
**Pattern (Annahme = limitiert):**
```javascript
// Wenn Use-Case Hardware-Bundles sind (echter Bestand erwartbar),
// reicht oft die Annahme stockLimitation=1:
const componentLimit = c => Math.floor((c.netStock ?? 0) / Math.max(1, c.quantity ?? 1));
const paketBestand = Math.min(...components.map(componentLimit));
```
**Wenn echtes `stockLimitation` zwingend ist:** zusätzlicher GET pro Komponenten-Variation (`/rest/items/{itemId}/variations/{variationId}`) — Vorsicht mit Roundtrip-Anzahl bei vielen Bundles.
**Entdeckt:** 2026-05-01 — beim Aufbau einer Paket-Bestand-Berechnung über alle Bundles eines Bereichs. Initial mit `stockLimitation: 0`-Default getestet → Funktion lieferte immer `null`, weil ein Default 0 (=unbegrenzt) jeden Komponenten als "∞" einstuft.
---