Adobe Commerce


Integration von Adobe Commerce MSI mit externen ERP-Systemen


Author Photo
Oleg Blinnikov
Senior Developer at comwrap

Adobe Commerce MSI Multi Source Inventory

Das Multi-Source Inventory (MSI) Projekt wurde entwickelt, um die Lagerverwaltung an mehreren Standorten zu ermöglichen, so dass Händler ihren physischen Bestand in Magento korrekt abbilden können, ohne Erweiterungen oder Anpassungen verwenden zu müssen.

MSI ist ein vollständig von der Community getragenes Projekt und wurde ab der Version 2.3 in den Magento Core integriert.

Seit vielen Jahren haben wir an vielen verschiedenen Integrationen gearbeitet, die Multi-Store-Inventar verwenden, und jetzt möchte ich die Erfolgsgeschichte einer der größten Herausforderungen erzählen.

In einem der Projekte sahen wir uns mit der Situation konfrontiert, dass alle Bestandsdaten aus dem externen System geladen werden mussten und wir alle Verkaufsprozesse in Adobe Commerce verwalten mussten, ohne die Bestandsinformationen zu speichern.

Werfen wir also einen Blick auf eine solche Integration vom technischen Standpunkt aus.

Zur Veranschaulichung der Codebeispiele wird eine mit Magento 2.4.2 kompatible Syntax verwendet. Das Protokoll der Interaktion mit ERP ist REST / JSON API, ein zweiseitiger Datenaustausch.

Zunächst ist es notwendig, die Funktionalität und die Anforderungen an das System zu bestimmen:

  • Implementierung eines Bestands-API-Dienstes für den Empfang von Bestandsdaten aus dem ERP
  • MSI so erweitern, dass nur Bestände aus dem externen ERP verwendet und alle anderen MSI-Bedingungen deaktiviert werden.
  • Abfrage der Lagerverfügbarkeit im Live-Modus vom ERP auf den Seiten Kategorie, PDP, Warenkorb und Schnellbestellung.

Im Rahmen dieses Artikels lassen wir die Erklärungen zu den Komponenten weg, die nicht direkt mit dem vorliegenden Thema zusammenhängen.


Implementierung des Stock-API-Dienstes für den Empfang von Bestandsdaten aus dem ERP

Es gibt mehrere Erweiterungspunkte von MSI, die wir nutzen können, um die Integration externer Lagerdienste zu erreichen.

Zunächst einmal gibt es zwei Bedingungsketten in Magento, die da wären:

/**
* Service which detects whether Product is salable for a given Stock (stock data + reservations)
**/
Magento\InventorySales\Model\IsProductSalableCondition\IsProductSalableConditionChain

/**
 * Service which detects whether a certain Qty of Product is salable for a given Stock (stock data + reservations)
**/
Magento\InventorySales\Model\IsProductSalableForRequestedQtyCondition\IsProductSalableForRequestedQtyConditionChain

Zunächst einmal gibt es zwei Bedingungsketten in Magento, die da wären:

  <type name="Magento\InventorySales\Model\IsProductSalableCondition\IsProductSalableConditionChain">
       <arguments>
            <argument name="conditions" xsi:type="array">
                <item name="stock_api_is_in_stock" xsi:type="array">
                    <item name="sort_order" xsi:type="number">90</item>
                    <item name="object" xsi:type="object">Comwrap\ProductStock\Model\IsProductSalableCondition</item>
                </item>
            </argument>
        </arguments>
    </type>
    <type name="Magento\InventorySales\Model\IsProductSalableForRequestedQtyCondition\IsProductSalableForRequestedQtyConditionChain">
        <arguments>
            <argument name="conditions" xsi:type="array">
                <item name="stock_api_is_correct_qty" xsi:type="array">
                    <item name="sort_order" xsi:type="number">90</item>
                    <item name="object" xsi:type="object">Comwrap\ProductStock\Model\IsCorrectQtyConditionApi</item>
                </item>
            </argument>
        </arguments>
    </type>

Bei Implementierungen erstellen wir Klassen, die Folgendes implementieren

Magento\InventorySalesApi\Api\IsProductSalableInterface

und

Magento\InventorySalesApi\Api\IsProductSalableForRequestedQtyInterface

Als dieser Artikel veröffentlicht wurde, waren diese beiden Schnittstellen bereits als veraltet gekennzeichnet.
Bitte prüfen Sie daher Magento\InventorySalesApi\Api\AreProductsSalableInterface und Magento\InventorySalesApi\Api\AreProductsSalableForRequestedQtyInterface

In execute(string $sku, int $stockId) und execute(string $sku, int $stockId, float $requestedQty): ProductSalableResultInterface Methoden müssen Sie die Implementierung Ihrer externen Quelle hinzufügen.

Technisch gesehen spielt es keine Rolle, woher die Daten stammen, Sie können einen externen Dienst, ein ERP, eine *.csv-Datei oder eine Datenbank als Quelle der Wahrheit hinzufügen.


Weitere Consitions in der Kette deaktivieren

In unserem Fall wollen wir keine Abhängigkeit von anderen Bedingungen in der Kette haben, also haben wir sie entfernt und einen Fallback für den Fall geschaffen, dass die Funktion deaktiviert ist.

<?xml version="1.0"?>
<!--
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Magento\InventorySales\Model\IsProductSalableCondition\IsProductSalableConditionChain"
                type="Comwrap\ProductStock\Model\IsProductSalableCondition\IsProductSalableConditionChain" />
    <preference for="IsProductSalableForRequestedQtyConditionChainOnAddToCart"
                type="Comwrap\ProductStock\Model\IsProductSalableForRequestedQtyCondition\IsProductSalableForRequestedQtyConditionChain" />
</config>

Diese Maßnahme ist nicht erforderlich, alles hängt von Ihren Anforderungen ab.

Das neue Chain-File:

<?php
declare(strict_types=1);

namespace Comwrap\ProductStock\Model\IsProductSalableCondition;

use Magento\InventorySales\Model\IsProductSalableCondition\IsProductSalableConditionChain as ConditionChain;

/**
 * Class IsProductSalableConditionChain
 * Overrides Condition Chain to work only with one condition
 */
class IsProductSalableConditionChain extends ConditionChain
{
    /**
     * @var array
     */
    private $condition;

    /**
     * @param array $conditions
     */
    public function __construct(
        array $conditions
    ) {
        if (isset($conditions['stock_api_is_in_stock'])) {
            $this->condition['stock_api_is_in_stock'] = $conditions['stock_api_is_in_stock'];
        } else {
            $this->condition = $conditions;
        }
        parent::__construct($this->condition);
    }
}


Update Product Qty

Eine weitere Änderung, die wir vornehmen müssen, ist die Festlegung der Menge für das Produkt bei der Abfrage des Bestands.

Dies kann durch das Plugin auf Magento\InventorySales\Model\GetProductSalableQty class erreicht werden:

<type name="Magento\InventorySales\Model\GetProductSalableQty">
        <plugin sortOrder="1" name="comwrapProductStockGetProductSalableQty"
                type="Comwrap\ProductStock\Plugin\Model\GetProductSalableQtyPlugin"/>
    </type>

Comwrap\ProductStock\Plugin\Model\GetProductSalableQtyPlugin:

<?php
declare(strict_types=1);

namespace Comwrap\ProductStock\Plugin\Model;

use Comwrap\ProductStock\Model\ProductStock;
use Magento\InventorySales\Model\GetProductSalableQty;

/**
 * Class GetProductSalableQtyPlugin
 * Plugin for updating salable qty of product
 */
class GetProductSalableQtyPlugin
{
    /**
     * @var ProductStock
     */
    private $productStock;

    /**
     * GetProductSalableQtyPlugin constructor.
     * @param ProductStock $productStock
     */
    public function __construct(
        ProductStock $productStock
    ) {
        $this->productStock = $productStock;
    }

    /**
     * @param GetProductSalableQty $subject
     * @param callable $proceed
     * @param string $sku
     * @param int $stockId
     * @return float
     * @noinspection PhpUnusedParameterInspection
     */
    public function aroundExecute(GetProductSalableQty $subject, callable $proceed, string $sku, int $stockId): float
    {
        $result = $proceed($sku, $stockId);
        return $this->productStock->getProductSalableQty($sku);
    }
}

Nach diesen grundlegenden Erweiterungen bestehender Mechanismen können Sie bereits die Bestandsquelle durch einen benutzerdefinierten Ort ersetzen.


Fazit

Das Magento MSI-Modul war eines der ersten Module, das dem Ansatz der Service-Dekomposition folgte. Gleichzeitig wurde es so entwickelt, dass Entwickler die Möglichkeit haben, es auf einfachste Weise zu erweitern. Zu Beginn unseres Projekts hatten wir mit viel mehr Arbeit gerechnet, aber nachdem wir uns mit der Architektur des Moduls beschäftigt hatten, wurden alle unsere Änderungen nur durch die Verwendung von Erweiterungspunkten vorgenommen.

Sie müssen sich immer noch um einige kleinere Aufgaben kümmern, wie z.B. die Integration einer API-Verbindung zu einem externen System oder die Änderung von Vorlagen, falls sich Ihr Bestand sehr häufig ändert und Sie nicht jedes Mal den PDP-/Kategorie-Cache bereinigen wollen, wenn dies geschieht.


Photo by Martin Adams on Unsplash