Der B2B-Verhandlungs-Workflow von der Anfrage bis zum Auftrag
Negotiable Quotes verwandeln den Magento-Warenkorb in einen mehrstufigen Verhandlungsprozess zwischen Firmenkunde und Vertrieb, mit eigener Statusmaschine, eigenen Berechtigungen und eigenem Datenmodell. Wer das Feature erweitern will, muss verstehen, wie ein Quote durch NegotiableQuoteManagement wandert und wo sich eigene Statusübergänge und Benachrichtigungen sauber einhängen lassen.
Inhaltsverzeichnis
- 1. Negotiable Quotes im B2B-Modul einordnen
- 2. Das Datenmodell hinter der Verhandlung
- 3. Die Statusmaschine: Von created bis ordered
- 4. Rollen und Berechtigungen im Verhandlungsprozess
- 5. Verhandlungen über die GraphQL-API steuern
- 6. Eigene Statusübergänge per Plugin ergänzen
- 7. Eigene Benachrichtigungen bei Statuswechseln
- 8. Preisgrenzen und Verhandlungsspielraum absichern
- 9. Betrieb: Ablauf, Cron und Datenmengen
- 10. Zusammenfassung
- 11. FAQ
1. Negotiable Quotes im B2B-Modul einordnen
Negotiable Quotes ist Teil der B2B-Suite von Adobe Commerce und erweitert den regulären Warenkorb um einen mehrstufigen Verhandlungsprozess zwischen Firmenkunde und Vertrieb. Technisch baut das Feature auf der bestehenden Quote-Entity auf, ergänzt sie aber um eine eigene Tabelle für Verhandlungsdaten, sodass ein Angebot nicht nur ein Warenkorb-Snapshot ist, sondern ein Zustand mit eigener Historie und eigenem Lebenszyklus.
Sichtbar wird das Feature erst über die Company-Struktur: Nur Kunden mit aktiver Zuordnung zu einer Company und der passenden Berechtigung können eine Anfrage als Negotiable Quote einreichen. Wer eigene Erweiterungen für den Verhandlungsprozess bauen will, kommt an drei zentralen Bausteinen nicht vorbei: dem Datenmodell rund um die Tabelle negotiable_quote, der Statusmaschine in Magento\NegotiableQuote\Model\Quote\Status und dem Service Contract NegotiableQuoteManagementInterface, über den nahezu jede Statusänderung läuft.
Ein wichtiger Unterschied zum normalen Checkout: Der Kunde bestellt bei einem Negotiable Quote nicht direkt, sondern reicht eine Anfrage ein, die intern verändert werden kann, bevor überhaupt eine Bestellung entsteht. Diese Zwischenstufe ist der eigentliche Kern des Features und der Grund, warum es ein eigenes Datenmodell statt einer einfachen Erweiterung des Checkouts braucht.
2. Das Datenmodell hinter der Verhandlung
Ein Negotiable Quote ist kein eigenständiges Entity im klassischen Sinn, sondern eine Erweiterung der Sales-Quote um die Tabelle negotiable_quote. Diese Tabelle hält quote_id als Fremdschlüssel sowie verhandlungsspezifische Felder wie status, negotiated_price_type, negotiated_price_value, shipping_price, expiration_period und quote_name. Der eigentliche Warenkorb mit seinen Positionen bleibt vollständig in quote und quote_item, nur die Verhandlungsmetadaten liegen separat.
Diese Trennung hat einen praktischen Grund: Preisüberschreibungen auf Zeilenebene werden nicht in negotiable_quote gespeichert, sondern über zusätzliche Spalten an quote_item angehängt, etwa original_custom_price_amount und original_discount_amount, damit der ursprüngliche Katalogpreis neben dem verhandelten Preis erhalten bleibt. Wer eigene Reports oder Exporte über verhandelte Preise bauen will, muss beide Tabellen gemeinsam auswerten, nicht nur negotiable_quote isoliert betrachten.
3. Die Statusmaschine: Von created bis ordered
Der Kern des Verhandlungs-Workflows ist eine klar definierte Statusmaschine, deren Konstanten in Magento\NegotiableQuote\Model\Quote\Status liegen. Ein neuer Quote startet im Status created, sobald ein Firmenkunde eine Anfrage einreicht wechselt er zu submitted_by_customer, und sobald der Vertrieb reagiert zu processing_by_admin oder submitted_by_admin. Am Ende steht entweder ordered, wenn der Kunde das finale Angebot annimmt, oder declined beziehungsweise expired, wenn keine Einigung zustande kommt.
Wichtig für eigene Erweiterungen ist, dass nicht jeder Status von jedem anderen aus erreichbar ist. Die erlaubten Übergänge sind in der Verwaltungslogik fest verdrahtet, und ein Versuch, einen Status zu setzen, der von der aktuellen Position aus nicht erlaubt ist, führt zu einer LocalizedException. Eigene Automatisierungen, etwa ein automatischer Statuswechsel nach Ablauf einer Frist, müssen sich also an dieselben Übergangsregeln halten wie die Standardoberfläche.
<?php
declare(strict_types=1);
namespace Mironsoft\NegotiableQuoteExtension\Model;
use Magento\NegotiableQuote\Model\Quote\Status;
/**
* Ermittelt die aus dem aktuellen Verhandlungsstatus heraus erlaubten Folgezustände.
*/
class AllowedTransitions
{
private const TRANSITIONS = [
Status::STATUS_CREATED => [Status::STATUS_SUBMITTED_BY_CUSTOMER],
Status::STATUS_SUBMITTED_BY_CUSTOMER => [
Status::STATUS_PROCESSING_BY_ADMIN,
Status::STATUS_DECLINED,
],
Status::STATUS_PROCESSING_BY_ADMIN => [
Status::STATUS_SUBMITTED_BY_ADMIN,
Status::STATUS_DECLINED,
],
Status::STATUS_SUBMITTED_BY_ADMIN => [
Status::STATUS_ORDERED,
Status::STATUS_SUBMITTED_BY_CUSTOMER,
Status::STATUS_EXPIRED,
],
];
/**
* Liefert die aus dem übergebenen Status heraus erlaubten Folgezustände.
*
* @param string $currentStatus
* @return string[]
*/
public function getAllowedNextStatuses(string $currentStatus): array
{
return self::TRANSITIONS[$currentStatus] ?? [];
}
}
4. Rollen und Berechtigungen im Verhandlungsprozess
Wer welchen Statuswechsel auslösen darf, hängt von zwei Berechtigungsebenen ab: der klassischen Magento-ACL auf Adminseite und den Company-Rollen auf Kundenseite. Auf Adminseite steuert die Ressource Magento_NegotiableQuote::manage, ob ein Vertriebsmitarbeiter überhaupt Zugriff auf die Negotiable-Quotes-Verwaltung hat, während feinere Ressourcen einzelne Aktionen wie das Setzen eines finalen Preises absichern.
Auf Kundenseite bestimmt die Company-Rolle, ob ein Firmenmitglied überhaupt einen Negotiable Quote anlegen, kommentieren oder final bestellen darf. Ein reiner Einkäufer kann in vielen Setups Anfragen stellen, aber nicht die finale Bestellung auslösen, das bleibt einem genehmigenden Approver vorbehalten. Diese Trennung folgt demselben Berechtigungsmodell wie die allgemeine Company-Struktur, wird für Negotiable Quotes aber um eine eigene Ressource ergänzt, die getrennt von den allgemeinen Bestellrechten vergeben wird.
5. Verhandlungen über die GraphQL-API steuern
Für Storefront-Integrationen jenseits der Standardoberfläche stellt das Modul Magento_NegotiableQuoteGraphQl eigene Mutations bereit. Mit createNegotiableQuote lässt sich aus einem bestehenden Warenkorb ein neuer Verhandlungsprozess starten, updateNegotiableQuote passt Kommentare, Preise oder den Status an, und die Query negotiableQuote liefert den aktuellen Verhandlungsstand samt Historie zurück.
Für eigene Frontend-Erweiterungen, etwa ein individuelles Verhandlungs-Dashboard, ist wichtig, dass die GraphQL-Schicht dieselben Berechtigungsprüfungen durchläuft wie die Storefront-Oberfläche. Ein Kunde ohne die passende Company-Rolle bekommt bei einem Statuswechsel-Versuch über die API denselben Autorisierungsfehler wie im UI, was eigene Custom-Frontends vor inkonsistentem Verhalten schützt, aber auch bedeutet, dass Rollenprüfungen nicht auf Frontend-Seite dupliziert werden müssen.
mutation UpdateNegotiableQuoteStatus {
updateNegotiableQuote(
input: {
quote_uid: "MTIz"
comment: { comment: "Rabatt auf 12% angepasst, Freigabe erbeten." }
status: SUBMITTED_BY_ADMIN
}
) {
quote {
uid
name
status
negotiated_price {
value
}
}
}
}
6. Eigene Statusübergänge per Plugin ergänzen
Um zusätzliche Logik bei einem Statuswechsel auszuführen, etwa eine interne Freigabe ab einem bestimmten Rabattschwellenwert, bietet sich ein Plugin auf NegotiableQuoteManagementInterface an. Ein around-Plugin kann vor dem eigentlichen Statuswechsel prüfen, ob die Bedingungen für den Übergang erfüllt sind, und bei Bedarf eine eigene Exception werfen, die den Wechsel blockiert, bevor die Standardlogik überhaupt greift.
Wichtig ist, dass ein solches Plugin die ursprüngliche Methode weiterhin sauber aufruft, wenn die eigene Prüfung erfolgreich war, damit die Kernlogik von Magento intakt bleibt und spätere Updates des B2B-Moduls nicht durch übersprungene Schritte kollidieren. Für rein beobachtende Zwecke, etwa Logging, ist ein after-Plugin die robustere Wahl, weil es die Kernlogik nicht verändern kann.
<!-- app/code/Mironsoft/NegotiableQuoteExtension/etc/di.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\NegotiableQuote\Api\NegotiableQuoteManagementInterface">
<plugin name="mironsoft_approval_threshold"
type="Mironsoft\NegotiableQuoteExtension\Plugin\ApprovalThresholdPlugin"
sortOrder="10"/>
</type>
</config>
7. Eigene Benachrichtigungen bei Statuswechseln
Die Standardbenachrichtigungen von Negotiable Quotes decken die üblichen Fälle ab, etwa eine E-Mail an den Kunden, wenn der Vertrieb ein Gegenangebot einreicht. Für projektspezifische Kanäle, etwa eine Nachricht an das Vertriebsteam über ein internes Tool oder einen Eintrag in ein CRM, reicht die Standardlogik nicht aus und lässt sich am saubersten über ein after-Plugin auf derselben Management-Methode nachrüsten.
Der Vorteil eines Plugins gegenüber einem eigenen Observer ist, dass der neue und der alte Status im selben Methodenaufruf bekannt sind, ohne dass man den vorherigen Zustand separat aus der Datenbank nachladen muss. Für asynchrone Zustellung, etwa wenn der externe Dienst gelegentlich nicht erreichbar ist, empfiehlt es sich, die eigentliche Benachrichtigung über eine Message Queue zu entkoppeln, statt sie synchron im Plugin auszuführen und damit den kompletten Statuswechsel zu verzögern.
8. Preisgrenzen und Verhandlungsspielraum absichern
Ohne zusätzliche Absicherung kann ein Vertriebsmitarbeiter im Prinzip jeden beliebigen Preis in ein Gegenangebot eintragen, was in größeren Teams schnell zu inkonsistenten Rabatten führt. Eine gängige Erweiterung ist eine eigene Validierung, die den negotiated_price_value gegen eine hinterlegte Untergrenze prüft, bevor der Statuswechsel zu submitted_by_admin überhaupt zugelassen wird.
Diese Prüfung lässt sich sauber in dasselbe around-Plugin integrieren, das bereits die Statusübergänge absichert, sollte fachlich aber getrennt bleiben: Eine Verletzung der Preisgrenze ist ein anderer Fehlerfall als ein ungültiger Statusübergang und verdient eine eigene, klar formulierte Fehlermeldung, damit der Vertrieb sofort erkennt, woran die Freigabe gescheitert ist, statt eine generische Exception zu erhalten.
9. Betrieb: Ablauf, Cron und Datenmengen
Negotiable Quotes, die nicht innerhalb der konfigurierten Frist beantwortet werden, laufen automatisch in den Status expired, gesteuert über einen Cron-Job, der abgelaufene Quotes regelmäßig durchsucht. Bei Shops mit vielen parallelen Verhandlungen lohnt sich ein Blick auf die Indizierung der negotiable_quote-Tabelle, insbesondere auf status und expiration_period, damit der Cron-Lauf auch bei wachsender Historie performant bleibt.
Da abgelehnte und abgelaufene Quotes standardmäßig nicht automatisch gelöscht werden, wächst die Tabelle über die Zeit kontinuierlich. Für Shops mit hohem Verhandlungsvolumen empfiehlt sich eine eigene Aufräumroutine, die alte, abgeschlossene Verhandlungen nach einer definierten Aufbewahrungsfrist archiviert oder entfernt, statt sie unbegrenzt in der produktiven Tabelle zu belassen und damit spätere Reports und Admin-Grids unnötig zu verlangsamen.
| Status | Ausgelöst durch | Typische Aktion | Mögliche Folgezustände |
|---|---|---|---|
created |
Kunde legt Anfrage an | Warenkorb in Verhandlung umwandeln | submitted_by_customer |
submitted_by_customer |
Kunde reicht Anfrage ein | Vertrieb prüft Anfrage | processing_by_admin, declined |
processing_by_admin |
Vertrieb bearbeitet Angebot | Preise und Konditionen anpassen | submitted_by_admin, declined |
submitted_by_admin |
Vertrieb sendet Gegenangebot | Kunde prüft Angebot | ordered, submitted_by_customer, expired |
ordered |
Kunde bestätigt Angebot | Bestellung wird erzeugt | Endzustand |
expired |
Frist ohne Reaktion abgelaufen | Verhandlung automatisch schließen | Endzustand |
Mironsoft
Magento-Entwicklung, Modul-Beratung und Systemarchitektur
Magento-Projekt, das eine zweite Meinung oder erfahrene Umsetzung braucht?
Wir entwickeln individuelle Magento-Module, beraten bei Architekturentscheidungen und übernehmen komplexe Umsetzungen, von der Service-Contract-Planung bis zum produktionsreifen Deployment.
Architektur-Beratung
Modul- und Systemarchitektur vor der Umsetzung fundiert durchdenken lassen.
Custom-Modul-Entwicklung
Individuelle Magento-Module nach Best Practices sauber umsetzen.
Code-Review & Audit
Bestehende Module auf Performance, Sicherheit und Wartbarkeit prüfen lassen.
10. Zusammenfassung
Negotiable Quotes Workflow
Datenmodell
negotiable_quote erweitert die Sales-Quote um Verhandlungsmetadaten, Preisüberschreibungen liegen zusätzlich an quote_item.
Statusmaschine
Feste Übergänge von created über submitted_by_customer und submitted_by_admin bis ordered, declined oder expired.
Erweiterbarkeit
Plugins auf NegotiableQuoteManagementInterface für eigene Freigaben, Preisgrenzen und Benachrichtigungen.
Betrieb
Cron schließt abgelaufene Quotes automatisch, eine eigene Aufräumroutine für alte Historie ist empfehlenswert.