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 →