S SISL docs EN

Przepływy synchronizacji

Sync produktów (Optima → Magento)

Trigger: cron sisl_optima_sync_products (dropdown harmonogramu, domyślnie co 1h) lub ręcznie z Dashboard.

ProductSync.run()
  ├─ license.isValidNow()? ──── NO ──► abort z errors=["LICENCJA NIEWAZNA"]
  │                              YES
  ▼
  pętla po stronach, pageSize z konfiguracji (200):
    SyncClient.get('/v1/products?page=N&pageSize=200&tier=1')
        │
        ▼
    HMAC sign + retry 3x → microservice → SQL:
        SELECT Twr_Kod, Twr_Nazwa, Twr_Jm, Twr_Ean,
               CASE WHEN TwC_Typ=1 THEN TwC_Wartosc END AS PriceNet,
               CASE WHEN TwC_Typ=2 THEN TwC_Wartosc END AS PriceGross
        FROM CDN.Towary t LEFT JOIN CDN.TwrCeny c
            ON c.TwC_TwrID = t.Twr_GIDNumer AND c.TwC_TwCNumer = @tier
        WHERE t.Twr_GIDTyp = 16
        OFFSET @offset ROWS FETCH NEXT @pageSize ROWS ONLY
        │
        ▼
    dla każdego SKU:
      ├─ filtr SKU prefix (opcjonalny)
      ├─ skip jeśli usługa (jm=GODZ, twr_typ != 0)
      ├─ skip jeśli atrybut sisl_optima_sync_disabled = 1
      ▼
      upsert do catalog_product_entity:
        - nowy → SKU + nazwa + status (config) + visibility (config) + ean + jm + cena (jeśli sync_cen ON, inaczej 0)
        - istniejący → tylko nazwa + ean + jm (cena tylko jeśli sync_cen ON)
        - description: nie nadpisuje istniejącego

Wynik: {"created": N, "updated": M, "skipped": K, "errors": [...]} zapisywany w core_config_data (SyncState) i pokazywany w Dashboard.

Sync stanów (Optima → Magento)

Trigger: cron sisl_optima_sync_stock (domyślnie co 15 min) lub ręcznie.

StockSync.run()
  └─ license gate ── j.w.
  ▼
  SyncClient.get('/v1/stocks?warehouse=MAGAZYN&page=N&pageSize=500')
      │
      ▼
  microservice → SQL (real stock z dokumentów PZ):
      SELECT t.Twr_Kod AS Sku, m.Mag_Symbol AS Warehouse,
             SUM(z.TwZ_Ilosc - z.TwZ_Quan) AS Available,
             MAX(i.TwI_Rezerwacje) AS Reserved,
             MAX(i.TwI_Zamowienia) AS OnOrder
      FROM CDN.Towary t
      JOIN CDN.Magazyny m ON m.Mag_Symbol = @warehouse
      LEFT JOIN CDN.TwrZasoby z
          ON z.TwZ_TwrId = t.Twr_GIDNumer AND z.TwZ_MagId = m.Mag_MagId
          AND z.TwZ_TrSIdDost > 0    -- TYLKO real dokumenty, ignoruje syntetyczne wpisy
      LEFT JOIN CDN.TwrIlosci i ON i.TwI_TwrId = t.Twr_GIDNumer
      GROUP BY t.Twr_Kod, m.Mag_Symbol
      ▼
  dla każdego SKU:
    rawQty   = Available z Optima
    effective = max(0, rawQty - stock_buffer)
    inStock  = effective > oos_threshold
    StockRegistry.updateStockItemBySku(sku, {qty: effective, isInStock: inStock})

Source of truth: CDN.TwrZasoby filtrowane przez TwZ_TrSIdDost > 0 (czyli tylko stany pokryte realnymi dokumentami PZ/PW/MM). To dokładnie ta sama logika której używa Optima GUI w widoku "Stan handlowy".

Forward zamówienia (Magento → Optima)

Trigger: Magento event sales_order_place_after (observer w events.xml).

OrderPlaceAfter.execute(observer)
  └─ license gate
  └─ orders_enabled config gate
  ▼
  OrderForward.send(order):
      buduje payload:
        {
          magentoOrderId, incrementId, createdAt, currency,
          grandTotal, subtotal, tax, shippingTotal, shippingMethod, paymentMethod,
          customer: {email, firstname, lastname, taxId, isCompany, magentoId},
          billingAddress: {...},
          shippingAddress: {...},
          items: [{sku, name, qty, priceNet, priceGross, taxAmount, taxPercent}, ...]
        }
      ▼
      SyncClient.post('/v1/orders', payload)
          ▼
      microservice:
          - waliduje payload
          - sprawdza license + HMAC
          - generuje XML zgodny z Optima Import (TraNag + TraElem + KntKarty)
          - wrzuca do folderu monitowanego przez Optima (XML import in)
          - zwraca {optimaInvoiceId} po asynchronicznym przetwarzeniu

Fail-soft: jeśli wysłanie nie powiedzie się (microservice offline, license invalid, etc.) — zamówienie zostaje w Magento bez śladu w Optimie. Plugin loguje warning + zapisuje komentarz w order status history. Cron retry można dodać w v0.2.

Sprawdzanie licencji

Trigger: cron sisl_optima_check_license (codziennie 03:00 domyślnie).

LicenseChecker.checkNow()
  buduje request:
    {
      key: <license key z config>,
      product: "sisl_optima",
      host: <BASE_URL host>,
      version: "0.1.0",
      nonce: <16 bajtów random hex>,
      manifest_hash: <SHA256 z 8 krytycznych plików>
    }
    ▼
  POST https://license.sisl.pl/check
    ▼
  serwer SISL:
    - looking up licencji
    - sprawdza status (active / revoked / expired)
    - sprawdza valid_until
    - sprawdza host binding (pierwszy raz: bind; potem: must match)
    - rejestruje manifest hash + porównuje z poprzednim (loguje rozjazd)
    - podpisuje Ed25519 response z echoed nonce
    ▼
  plugin:
    - WERYFIKUJE PODPIS Ed25519 (BEZ TEGO STATE NIE ZAPISANY)
    - sprawdza nonce match (anti-replay)
    - zapisuje state do core_config_data (sisl_optima/license/state):
        {valid, message, valid_until, checked_at, verified: true, was_valid}

Cykl życia produktów

Akcja w Optimie Co się dzieje w Magento
Dodajesz nowy SKU w cenniku Następny ProductSync utworzy go w Magento (status=Enabled, cena=0 jeśli prices_enabled=No)
Zmieniasz cenę w cenniku 1 StockSync NIE rusza; ProductSync nadpisze tylko jeśli prices_enabled=Yes
Wpisujesz PZ na 50 szt Następny StockSync ustawi qty=50, in_stock=1
Wystawiasz FA (sprzedaż) na 5 szt TwrZasoby decrementuje; następny StockSync ustawi qty=45
Oznaczasz towar nieaktywny Plugin nadal go synchronizuje (Twr_NieAktywny nie jest sprawdzane v0.1 → todo v0.2)
Usuwasz towar Pozostaje w Magento (orphan); usuwaj ręcznie albo czekaj na v0.2

Następny krok: Diagnostyka i błędy →