Toplam görüntülenme: 46Günlük görüntülenme: 3

Magento 2, e-ticaret altyapısı olarak ne kadar güçlü olsa da, bir işletmenin operasyonel kalbi genellikle ERP (Kurumsal Kaynak Planlama) ve Muhasebe yazılımlarıdır. Stok yönetimi, sipariş takibi, faturalandırma, müşteri verisi ve finansal raporlama gibi kritik süreçler bu sistemlerde yönetilir.

Bu iki dünya arasında (E-Ticaret ve ERP) manuel veri girişi yapmak, hem zaman kaybıdır hem de insan hatasına açıktır. Çözüm, bu sistemleri birbirine bağlayan sağlam bir entegrasyon modülüdür.

Ancak, bir ERP entegrasyonu geliştirmek, Magento 2’deki en karmaşık görevlerden biridir. Sadece Magento mimarisini değil, aynı zamanda bağlanılacak ERP’nin API’sini (SOAP, REST, XML, dosya transferi vb.), veri modellerini ve iş akışlarını da derinlemesine anlamayı gerektirir.

Bu rehberde, bir Magento 2 ERP “Connector” (Bağlayıcı) modülünün temel konseptlerini, kullanılacak ana araçları (Observers ve Cron Jobs) ve basit kod örneklerini bulacaksınız.

Önemli Not: Bu rehber, temel yapı ve konseptleri göstermek amacıyla hazırlanmıştır. Gerçek dünyada bir ERP entegrasyonu (Logo, Netsis, SAP, Mikro, özel yazılımlar vb.), iki yönlü veri akışı, çakışma (conflict) yönetimi, performans optimizasyonu (binlerce ürünü senkronize etmek) ve detaylı hata takibi gibi çok daha karmaşık senaryolar içerir.

Yılmaz Soft olarak, bu tür karmaşık ve iş açısından kritik entegrasyonlar konusunda uzman bir ekibe sahibiz. İşletmenizin verimliliğini artıracak, güvenli ve performanslı Magento 2 – ERP entegrasyon çözümleri için profesyonel destek sunmaktayız.

1. Adım: Entegrasyon Stratejisi ve Modül Yapısı

Önce neyi, ne zaman ve nasıl senkronize edeceğimize karar vermeliyiz.

  • Siparişler: Yeni bir sipariş alındığında anında ERP’ye aktarılmalı mı? (Gerçek zamanlı)
  • Stok: ERP’deki stok değiştiğinde Magento’ya ne sıklıkta yansıtılmalı? (Toplu, örn: saatlik)
  • Müşteriler: Yeni üye olan müşteri ERP’de de oluşturulmalı mı? (Gerçek zamanlı)

Bu rehberde, “Sipariş anında gönder” (Observer kullanarak) ve “Stok saatlik güncelle” (Cron Job kullanarak) senaryolarına odaklanacağız.

Modülümüzün adı YilmazSoft_ErpConnector olsun.

Dosya: app/code/YilmazSoft/ErpConnector/registration.php

<?php
/**
 * Copyright © Yılmaz Soft. All rights reserved.
 */
use \Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(
    ComponentRegistrar::MODULE,
    'YilmazSoft_ErpConnector',
    __DIR__
);

Dosya: app/code/YilmazSoft/ErpConnector/etc/module.xml Sipariş (Magento_Sales) ve Ürün/Stok (Magento_Catalog) modüllerine bağımlılık ekliyoruz.

<?xml version="1.0"?>
<config xmlns:xsi="[http://www.w3.org/2001/XMLSchema-instance](http://www.w3.org/2001/XMLSchema-instance)" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="YilmazSoft_ErpConnector" setup_version="1.0.0">
        <sequence>
            <module name="Magento_Sales"/>
            <module name="Magento_Catalog"/>
            <module name="Magento_Customer"/>
        </sequence>
    </module>
</config>

2. Adım: Yapılandırma (Admin Panel Ayarları)

Entegrasyonun çalışması için API bilgileri ve ayarlar gereklidir.

Dosya: app/code/YilmazSoft/ErpConnector/etc/adminhtml/system.xml Admin panelinde Mağazalar > Yapılandırma altına ayar sayfamızı ekliyoruz.

<?xml version="1.0"?>
<config xmlns:xsi="[http://www.w3.org/2001/XMLSchema-instance](http://www.w3.org/2001/XMLSchema-instance)" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <tab id="yilmazsoft" translate="label" sortOrder="100">
            <label>Yılmaz Soft</label>
        </tab>
        <section id="erpconnector" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
            <label>ERP Entegrasyonu</label>
            <tab>yilmazsoft</tab>
            <resource>YilmazSoft_ErpConnector::config</resource>
            <group id="general" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>Genel Ayarlar</label>
                <field id="enable" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0">
                    <label>Entegrasyon Aktif</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                </field>
                <field id="api_endpoint" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0">
                    <label>ERP API Adresi (Endpoint)</label>
                    <comment>Örn: [https://erp.sirketim.com/api/v2/](https://erp.sirketim.com/api/v2/)</comment>
                </field>
                <field id="api_key" translate="label" type="obscure" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0">
                    <label>API Anahtarı (Token/Key)</label>
                    <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
                </field>
            </group>
            <group id="sync" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>Senkronizasyon Ayarları</label>
                <field id="sync_orders" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0">
                    <label>Siparişleri Senkronize Et</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                </field>
                <field id="sync_stock" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0">
                    <label>Stokları Senkronize Et</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                </field>
            </group>
        </section>
    </system>
</config>

3. Adım: Gerçek Zamanlı Sipariş Aktarımı (Observer)

Müşteri “Siparişi Tamamla” butonuna bastıktan hemen sonra siparişi ERP’ye göndereceğiz. Bunun için sales_order_place_after event’ini (olayını) dinleyeceğiz.

Dosya: app/code/YilmazSoft/ErpConnector/etc/events.xml

<?xml version="1.0"?>
<config xmlns:xsi="[http://www.w3.org/2001/XMLSchema-instance](http://www.w3.org/2001/XMLSchema-instance)" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="sales_order_place_after">
        <observer name="yilmazsoft_erpconnector_order_place_after" instance="YilmazSoft\ErpConnector\Observer\OrderPlaceAfter" />
    </event>
</config>

Dosya: app/code/YilmazSoft/ErpConnector/Observer/OrderPlaceAfter.php Bu sınıf, sipariş alındığında tetiklenecek ve API çağrısını başlatacak.

<?php

namespace YilmazSoft\ErpConnector\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\HTTP\Client\Curl;
use Magento\Framework\Serialize\Serializer\Json;
use Psr\Log\LoggerInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;

class OrderPlaceAfter implements ObserverInterface
{
    protected $logger;
    protected $curl;
    protected $jsonSerializer;
    protected $scopeConfig;

    // Config (Ayarlar) dosyasındaki yolları tanımlayalım
    const XML_PATH_ERP_ENABLE = 'erpconnector/general/enable';
    const XML_PATH_ERP_API_ENDPOINT = 'erpconnector/general/api_endpoint';
    const XML_PATH_ERP_API_KEY = 'erpconnector/general/api_key';
    const XML_PATH_ERP_SYNC_ORDERS = 'erpconnector/sync/sync_orders';

    public function __construct(
        LoggerInterface $logger,
        Curl $curl,
        Json $jsonSerializer,
        ScopeConfigInterface $scopeConfig
    ) {
        $this->logger = $logger;
        $this->curl = $curl;
        $this->jsonSerializer = $jsonSerializer;
        $this->scopeConfig = $scopeConfig;
    }

    public function execute(Observer $observer)
    {
        // Ayarlardan modülün aktif olup olmadığını kontrol et
        $storeScope = \Magento\Store\Model\ScopeInterface::SCOPE_STORE;
        $isEnabled = $this->scopeConfig->getValue(self::XML_PATH_ERP_ENABLE, $storeScope);
        $isOrderSyncEnabled = $this->scopeConfig->getValue(self::XML_PATH_ERP_SYNC_ORDERS, $storeScope);

        if (!$isEnabled || !$isOrderSyncEnabled) {
            $this->logger->info('ERP Connector: Sipariş gönderimi pasif.');
            return $this;
        }

        try {
            /** @var \Magento\Sales\Model\Order $order */
            $order = $observer->getEvent()->getOrder();
            if (!$order) {
                return $this;
            }

            // API bilgilerini al
            $apiEndpoint = $this->scopeConfig->getValue(self::XML_PATH_ERP_API_ENDPOINT, $storeScope);
            $apiKey = $this->scopeConfig->getValue(self::XML_PATH_ERP_API_KEY, $storeScope); // Gerçekte decrypt edilmeli

            // ERP'ye gönderilecek veriyi hazırla (Basitleştirilmiş)
            $orderData = [
                'order_id' => $order->getIncrementId(),
                'total' => $order->getGrandTotal(),
                'customer_email' => $order->getCustomerEmail(),
                'items' => []
            ];

            foreach ($order->getAllItems() as $item) {
                $orderData['items'][] = [
                    'sku' => $item->getSku(),
                    'qty' => $item->getQtyOrdered(),
                    'price' => $item->getPrice()
                ];
            }

            // Veriyi JSON'a çevir
            $jsonBody = $this->jsonSerializer->serialize($orderData);

            // API İsteğini Gönder (Basit bir POST isteği)
            $this->curl->setOption(CURLOPT_TIMEOUT, 10); // 10 saniye timeout
            $this->curl->addHeader("Content-Type", "application/json");
            $this->curl->addHeader("Authorization", "Bearer " . $apiKey);
            
            // Sipariş gönderme endpoint'i
            $this->curl->post($apiEndpoint . 'orders', $jsonBody);

            $response = $this->curl->getBody();
            $httpStatusCode = $this->curl->getStatus();

            if ($httpStatusCode == 200 || $httpStatusCode == 201) {
                $this->logger->info('ERP Connector: Sipariş başarıyla gönderildi: ' . $order->getIncrementId());
                // Siparişe not ekle
                $order->addStatusHistoryComment('ERP sistemine başarıyla gönderildi. (Ref: ' . $httpStatusCode . ')');
                $order->save();
            } else {
                throw new \Exception("ERP API Hatası: " . $httpStatusCode . " - Cevap: " . $response);
            }

        } catch (\Exception $e) {
            $this->logger->critical('ERP Connector: Sipariş gönderim hatası: ' . $e->getMessage());
            // Hata olursa siparişe not düş, böylece manuel takip edilebilir.
            if (isset($order)) {
                 $order->addStatusHistoryComment('ERP SİSTEMİNE GÖNDERİLEMEDİ. Hata: ' . $e->getMessage());
                 $order->save();
            }
        }

        return $this;
    }
}

4. Adım: Toplu Stok Senkronizasyonu (Cron Job)

ERP’deki stokları Magento’ya çekmek için zamanlanmış görev (Cron Job) kullanacağız.

Dosya: app/code/YilmazSoft/ErpConnector/etc/crontab.xml Bu dosya, StockSync sınıfımızı ne sıklıkta çalıştıracağımızı belirler. (Örnek: Her saat başı)

<?xml version="1.0"?>
<config xmlns:xsi="[http://www.w3.org/2001/XMLSchema-instance](http://www.w3.org/2001/XMLSchema-instance)" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd">
    <group id="default">
        <job name="yilmazsoft_erpconnector_stocksync" instance="YilmazSoft\ErpConnector\Cron\StockSync" method="execute">
            <!-- Her saat başı çalışır -->
            <schedule>0 * * * *</schedule>
        </job>
    </group>
</config>

Dosya: app/code/YilmazSoft/ErpConnector/Cron/StockSync.php Bu sınıf, zamanı geldiğinde ERP’den stokları çeker ve Magento’da günceller.

<?php

namespace YilmazSoft\ErpConnector\Cron;

use Psr\Log\LoggerInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\HTTP\Client\Curl;
use Magento\Framework\Serialize\Serializer\Json;
use Magento\CatalogInventory\Api\StockRegistryInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;

class StockSync
{
    protected $logger;
    protected $scopeConfig;
    protected $curl;
    protected $jsonSerializer;
    protected $stockRegistry;
    protected $productRepository;

    // Config (Ayarlar) yolları
    const XML_PATH_ERP_ENABLE = 'erpconnector/general/enable';
    const XML_PATH_ERP_API_ENDPOINT = 'erpconnector/general/api_endpoint';
    const XML_PATH_ERP_API_KEY = 'erpconnector/general/api_key';
    const XML_PATH_ERP_SYNC_STOCK = 'erpconnector/sync/sync_stock';

    public function __construct(
        LoggerInterface $logger,
        ScopeConfigInterface $scopeConfig,
        Curl $curl,
        Json $jsonSerializer,
        StockRegistryInterface $stockRegistry,
        ProductRepositoryInterface $productRepository
    ) {
        $this->logger = $logger;
        $this->scopeConfig = $scopeConfig;
        $this->curl = $curl;
        $this->jsonSerializer = $jsonSerializer;
        $this->stockRegistry = $stockRegistry;
        $this->productRepository = $productRepository;
    }

    public function execute()
    {
        $this->logger->info('ERP Connector: Stok Senkronizasyonu Cron Başladı.');
        
        $storeScope = \Magento\Store\Model\ScopeInterface::SCOPE_STORE;
        $isEnabled = $this->scopeConfig->getValue(self::XML_PATH_ERP_ENABLE, $storeScope);
        $isStockSyncEnabled = $this->scopeConfig->getValue(self::XML_PATH_ERP_SYNC_STOCK, $storeScope);

        if (!$isEnabled || !$isStockSyncEnabled) {
            $this->logger->info('ERP Connector: Stok senkronizasyonu pasif.');
            return;
        }

        try {
            // API bilgilerini al
            $apiEndpoint = $this->scopeConfig->getValue(self::XML_PATH_ERP_API_ENDPOINT, $storeScope);
            $apiKey = $this->scopeConfig->getValue(self::XML_PATH_ERP_API_KEY, $storeScope);

            // ERP'den stokları çek (GET isteği)
            $this->curl->addHeader("Authorization", "Bearer " . $apiKey);
            $this->curl->get($apiEndpoint . 'stocks'); // Örnek endpoint
            
            $response = $this->curl->getBody();
            $httpStatusCode = $this->curl->getStatus();

            if ($httpStatusCode != 200) {
                 throw new \Exception("ERP API Hatası: " . $httpStatusCode . " - Cevap: " . $response);
            }

            $stocks = $this->jsonSerializer->unserialize($response);

            // $stocks formatı: [ {"sku": "SKU001", "qty": 150}, {"sku": "SKU002", "qty": 0} ]
            
            if (!is_array($stocks) || !isset($stocks[0]['sku'])) {
                $this->logger->warning('ERP Connector: Stok verisi beklenen formatta gelmedi.');
                return;
            }
            
            $updatedCount = 0;
            foreach ($stocks as $stockData) {
                try {
                    $sku = $stockData['sku'];
                    $qty = (int)$stockData['qty'];

                    // Magento'da ürünü bul ve stoğu güncelle
                    // Not: Bu yöntem çok fazla ürün için yavaştır. Gerçekte "mass update" gerekir.
                    $product = $this->productRepository->get($sku);
                    $stockItem = $this->stockRegistry->getStockItem($product->getId());
                    
                    $stockItem->setQty($qty);
                    $stockItem->setIsInStock($qty > 0); // Stok 0'dan büyükse "Stokta Var" yap
                    
                    $this->stockRegistry->updateStockItemBySku($sku, $stockItem);
                    $updatedCount++;

                } catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
                    $this->logger->warning('ERP Connector: SKU bulunamadı: ' . $sku);
                } catch (\Exception $e) {
                    $this->logger->error('ERP Connector: SKU güncelleme hatası: ' . $sku . ' - Hata: ' . $e->getMessage());
                }
            }
            
            $this->logger->info("ERP Connector: Stok Senkronizasyonu Tamamlandı. Güncellenen SKU sayısı: " . $updatedCount);

        } catch (\Exception $e) {
            $this->logger->critical('ERP Connector: Stok senkronizasyonu ana hatası: ' . $e->getMessage());
        }
    }
}

Profesyonel ERP Entegrasyonu: Neden Yılmaz Soft?

Yukarıdaki adımlar, bir entegrasyonun “Merhaba Dünya” seviyesidir. Gerçek bir iş operasyonunda, bu yapı hızla yetersiz kalacaktır.

Yılmaz Soft olarak, tam da bu noktada devreye giriyoruz. Başarılı bir ERP entegrasyonu için ele alınması gereken kritik konular:

  1. Performans ve Toplu İşlemler (Mass Operations): 100.000 ürünün stoğunu StockSync.php‘deki gibi tek tek (loop içinde) güncellemek, sunucuyu kilitler. Bunun yerine save() metodunu döngü dışında kullanan, doğrudan veritabanına yazan (Mass Update) veya Asenkron işlemler (Message Queue) kullanan özel ve performanslı yapılar kurarız.
  2. Veri Haritalama (Data Mapping): ERP’deki “Ürün Kodu” ile Magento’daki “SKU” her zaman aynı mıdır? “Müşteri Vergi Numarası” Magento’da hangi alana yazılacak? Özellikle “Yapılandırılabilir Ürünler” (Configurable Products) ve “Paket Ürünler” (Bundle Products) için karmaşık veri eşleştirme gerekir.
  3. Hata Yönetimi ve Kuyruk (Queue) Sistemi: Ya sipariş gönderirken ERP API’si anlık olarak kapalıysa? Observer içinde yaşanan bir hata, müşterinin ödeme ekranında donup kalmasına neden olabilir. Bu yüzden, gerçek zamanlı işlemleri bile bir “Kuyruk” (Message Queue) sistemine (Magento’da hazır bulunan RabbitMQ) aktarırız. Böylece işlem başarısız olursa, Magento otomatik olarak 3-5 dakika sonra tekrar dener.
  4. İki Yönlü (Bi-Directional) Senkronizasyon: Sadece ERP -> Magento değil, bazen Magento -> ERP yönüne de veri gerekir. Veya sipariş ERP’de “Kargolandı” olarak işaretlendiğinde, Magento’daki siparişin durumunu otomatik “Gönderildi” yapmak ve kargo takip numarası eklemek gerekebilir.
  5. Güvenlik ve Veri Bütünlüğü: ERP sistemleri kritik veriler içerir. API bağlantılarının güvenli (Token, OAuth2, IP Kısıtlaması) olması ve veri bütünlüğünün (örneğin, bir siparişin iki kez gönderilmemesi) garanti altına alınması şarttır.
  6. E-Fatura & E-Arşiv Entegrasyonu: Sipariş ERP’ye aktarıldıktan sonra, ilgili E-Fatura/E-Arşiv’in kesilip, PDF’inin Magento’daki müşteri paneline otomatik olarak yüklenmesi gibi gelişmiş otomasyonlar sağlarız.

Yılmaz Soft olarak, Logo, Netsis, SAP Business One, Mikro ve diğer özel ERP yazılımlarınız ile Magento 2 siteniz arasında tam entegrasyon sağlıyoruz. İşinizi otomasyona taşıyarak manuel iş yükünüzü sıfıra indirmek ve operasyonel verimliliğinizi maksimuma çıkarmak için bizimle iletişime geçin.

Sonuç

Magento 2 ERP entegrasyonu, bir e-ticaret işletmesinin büyümesi için bir lüks değil, zorunluluktur. Bu rehber, sürecin teknik temellerini atmak için bir başlangıç noktasıdır. Ancak başarılı, ölçeklenebilir ve hatasız bir operasyon için bu işi profesyonellere emanet etmek en akıllıca yatırımdır.

Bir yorum yapın