Dodawanie rodziny produktowej
Sposób działania
Import rodzin produktowych korzysta z tego samego dwuetapowego endpointu co produkty pojedyncze:
POST
/import/products/v2/load_data— wgrywa definicje (plik JSON) do bufora danych.continueUpload=1doklejam kolejne paczki,continueUpload=0czyści bufor przed wgraniem.POST
/import/products/v2/run— przenosi bufor do bazy produkcyjnej.
Podczas przenoszenia system sprawdza, czy dany kod (code wariantu) już istnieje. Jeśli istnieje — zostaje pominięty (import nie nadpisuje istniejących produktów).
To jest endpoint do DODAWANIA. Istniejące kody wariantów są pomijane, więc nie da się tym endpointem zaktualizować istniejącego wariantu (jego stock_enabled, cen, jednostek). Do aktualizacji istniejących produktów użyj POST /import/product/update — patrz sekcja Aktualizacja istniejących produktów.
Jak wysłać request
Endpoint load_data przyjmuje dane jako multipart/form-data — NIE jako surowe body JSON. Tablica obiektów (definicja rodziny + warianty) jest wysyłana jako plik w polu formularza file. {instancja} = adres API Twojej instancji Saly (np. https://twoja-domena/api).
Krok 1 — token. Wyślij POST na /user/token w formacie application/x-www-form-urlencoded z polami username oraz hasłem. W odpowiedzi otrzymasz access_token — wstaw go do nagłówka autoryzacji (typ Bearer) w kolejnych żądaniach.
Krok 2 — load_data (POST /import/products/v2/load_data), żądanie typu multipart/form-data:
Element żądania | Wartość |
|---|---|
Nagłówek autoryzacji | typ Bearer z tokenem z kroku 1 |
Pole formularza | plik JSON z tablicą (definicja rodziny + warianty), |
Pole formularza |
|
Krok 3 — run (POST /import/products/v2/run): nagłówek Bearer, parametry w query string: truncate_all_products oraz truncate_all_categories (zwykle oba 0). Odpowiedź zawiera report (patrz „Odpowiedź i raport").
Gotowe polecenia curl znajdziesz w zewnętrznej dokumentacji technicznej API. Najważniejsze: payload to plik w polu file (multipart), a nie surowe body JSON.
Rodzina produktów — dwa obiekty
Rodzinę opisują dwa rodzaje obiektów:
Definicja rodziny — „parasol": nazwa, cechy różnicujące (
col1–col15), kategorie, atrybuty. Nie jest osobno sprzedawanym produktem — nie ma własnej ceny, stanu ani jednostek.Wariant rodziny — właściwy, sprzedawalny produkt (ma
code, cenę, stan i jednostki sprzedażowe), powiązany z definicją przezfamily_idi opisany wartościami cech (col1–col15).
W jednym pliku importu umieść definicję rodziny oraz jej warianty. Definicja (dany family_id) musi istnieć, aby warianty mogły się do niej podpiąć. Cena, stan (stock_enabled) i jednostki sprzedażowe ustawiane są na WARIANTACH (każdy wariant osobno), nie na definicji.
Definicja rodziny — dokumentacja pól
Legenda kolumny typ: * wymagane · X znaki · [0-1] 0/1 · [0-9] liczby · R2 liczba z 2 miejscami · JSON · (x) limit znaków.
family
Pole | Opis | Przykład | Typ |
|---|---|---|---|
| Unikalny identyfikator rodziny produktowej. Po nim warianty podpinają się do rodziny. |
| X (255) * |
| Definicja cechy różnicującej (nazwa cechy) w językach, np. „Kolor", „Rozmiar". |
| JSON |
information
Pole | Opis | Typ |
|---|---|---|
| Nazwa rodziny w językach (pl, en, de, ru, es, fr, it) | JSON |
| Opis w językach (HTML dozwolony) | JSON |
| Publikacja na froncie (0 = nie [domyślnie], 1 = tak) | [0-1] |
| Promowanie (0 = nie [domyślnie], 1 = tak) | [0-1] |
metadata
Pole | Opis | Typ |
|---|---|---|
| Dane SEO w językach | JSON |
| Przyjazny URL w językach | JSON |
categories
Pole | Opis | Typ |
|---|---|---|
| Kategorie rodziny. Pierwsza = główna. Pełna ścieżka, separator | JSON |
manufacturer
Pole | Opis | Typ |
|---|---|---|
| Nazwa producenta | X (45) |
| URL do logo producenta | X (255) |
collective_packages (opakowania zbiorcze)
Pole | Opis | Przykład | Typ |
|---|---|---|---|
| Czy dostępne do zakupu w opakowaniach zbiorczych (0 = nie [domyślnie], 1 = tak) |
| [0-1] |
| Czy dostępne tylko w opakowaniach zbiorczych (0 = nie [domyślnie], 1 = tak) |
| [0-1] |
| Procentowa modyfikacja ceny |
| [0-9] |
| Czy rabat dodawany do ceny w pierwszej kolejności (0 / 1) |
| [0-1] |
attributes / multimedia
Pole | Opis | Typ |
|---|---|---|
| Parametry techniczne pogrupowane, w językach (format jak w przykładzie) | JSON |
| Lista URL-i zdjęć (bez limitu) | JSON |
| Lista par | JSON |
Przykładowy plik — definicja rodziny
[
{
"family": {
"family_id": "IMPORT-RODZIN-1",
"col1": { "pl": "Kolor", "en": "Color" },
"col2": { "pl": "Materiał", "en": "Material" },
"col3": { "pl": "Rozmiar", "en": "Size" }
},
"information": {
"name": { "pl": "MacBook Air 13", "en": "MacBook Air 13" },
"description": { "pl": "<p>opis rodziny</p>" },
"is_active": "1",
"is_promoted": "0"
},
"metadata": {
"metatitle": { "pl": "MacBook Air 13" },
"keywords": { "pl": "MacBook,Air,13" },
"metadescription": { "pl": "MacBook Air 13 - najlepsza cena" },
"slug": { "pl": "MacBook_Air_13" }
},
"categories": { "paths": [ { "pl": "Laptopy/Apple/MacBook Air 13" } ] },
"manufacturer": { "name": "Apple", "logo": "https://www.apple.com/logo.png" },
"collective_packages": {
"is_active": "0",
"is_only_packages": "0",
"price": { "discount": "10", "is_includes_the_charge": "0" }
},
"attributes": { "pl": [ { "Dane techniczne": [ ["Kolor", "czerwony"] ] } ] },
"multimedia": { "photos": [ "https://example.com/photo1.jpg" ], "files": [] }
}
]
Wariant rodziny — dokumentacja pól
Wariant to sprzedawalny produkt. Poza polami family ma te same sekcje co produkt pojedynczy: code, price, stock, units itd.
family
Pole | Opis | Przykład | Typ |
|---|---|---|---|
| Identyfikator rodziny, do której należy wariant (musi zgadzać się z definicją). |
| X (255) * |
| Nazwa wariantu rodziny |
| X (255) |
| Czy wariant aktywny (0 = nie, 1 = tak [domyślnie]) |
| [0-1] |
| Pozycja/kolejność wariantu na liście (niższa = wyżej) |
| [0-9] (3) |
| Wartość cechy różnicującej (odpowiadającej nazwie z definicji) w językach, np. „Czarny". |
| JSON |
code WYMAGANE
Opis | Przykład | Typ |
|---|---|---|
Unikalny kod produktu (wariantu) w Saly. Po nim sprawdzane jest, czy produkt już istnieje. |
| X (255) * |
additional_code
Pole | Opis | Typ |
|---|---|---|
| Numer EAN (13 cyfr) | [0-9] (13) |
| Stock Keeping Unit | X (45) |
| Kod produktu nadany przez dostawcę | X (255) |
price
Pole | Opis | Przykład | Typ |
|---|---|---|---|
| Cena katalogowa (sprzedażowa) netto — cena, od której liczone są rabaty i którą widzi klient w jednostce podstawowej. |
| R2 |
| Minimalna cena netto, za którą produkt może być sprzedany (próg). |
| R2 |
| Cena zakupu netto. |
| R2 |
| Stawka VAT (cecha produktu). |
| [0-9] |
energy_class
Pole | Opis | Typ |
|---|---|---|
| Klasa energetyczna | [A-G] (1) |
| Link do etykiety klasy energetycznej (PDF) | X (255) |
| Link do karty informacyjnej (PDF) | X (255) |
stock — stany magazynowe i flaga "stock impact" (na wariancie)
Pole | Opis | Przykład | Typ |
|---|---|---|---|
| Flaga „Włącz stany magazynowe dla produktu" (stock impact). |
| [0-1] |
Ważne o stock_enabled (dotyczy każdego wariantu osobno):
To tylko flaga (czy wariant śledzi stan), NIE ilość. Poziomy stanu wgrywa się osobnym importem stanów (
/import/product_stocks).Aktualizacja
stock_enabledistniejącego wariantu nie zadziała przez ten endpoint (istniejące kody są pomijane) — użyj/import/product/update.Jednostki sprzedażowe definiuj wyłącznie w bloku
units— polestock.unitnie istnieje i jest ignorowane.
delivery_date — czas dostawy per magazyn (tablica):
Pole | Opis | Przykład | Typ |
|---|---|---|---|
| Czas dostawy (w językach). Pusty → dziedziczony z magazynu. |
| JSON |
| Czas dostawy w godzinach |
| [0-9] |
| External ID magazynu |
| [0-9] |
| Spodziewana data uzupełnienia |
| date |
package
Pole | Opis | Typ |
|---|---|---|
| Nazwa opakowania zdefiniowanego w systemie | X (255) |
| Wymiary opakowania (cm) | R2 |
| Waga opakowania (kg) | R2 |
units — jednostki sprzedażowe i ich ceny (na wariancie)
Sekcja units to tablica jednostek wariantu. Każda jednostka odwołuje się (przez external_id) do jednostki zdefiniowanej w Słownikach → Jednostki sprzedażowe.
Model i ceny jednostek
Jednostka podstawowa (primary) — baza przeliczeń i jednostka stanu magazynowego; jej
rate= 1 (niezmienny).rate— ile jednostek podstawowych mieści jedna ta jednostka. Cena jednostki = cena katalogowa (srp_netto) ×rate.discount— rabat (%) dla tej jednostki.minimum_sales_quantity— minimalna ilość zamówienia w tej jednostce (całkowita > 0).Jednostka domyślna (default) — wstępnie wybrana; musi być aktywna.
active— czy jednostka widoczna/wybieralna.
Gwarancja integralności (per wariant): każdy wariant po imporcie ma dokładnie jedną aktywną jednostkę podstawową i jedną aktywną jednostkę domyślną. Gdy nie oznaczysz primary_unit=1 (albo jednostka jest nieprawidłowa), importer dobierze jednostkę podstawową ze słownikowych ustawień domyślnych — wariant nigdy nie zostanie bez jednostki podstawowej.
Brak sekcji units — zachowanie słownikowe
Sekcja units jest opcjonalna. Bez niej jednostki wariantu ustawiane są wg flag domyślności w Słowniku, honorowanych niezależnie:
Flaga w Słowniku | Ustawia na wariancie |
|---|---|
Domyślnie aktywna |
|
Domyślnie prezentowana |
|
Domyślna jednostka podstawowa |
|
Np. jednostka z Domyślnie aktywna = ON i Domyślnie prezentowana = OFF będzie aktywna, ale NIE domyślna.
Pola jednostki
Pole | Opis | Przykład | Typ |
|---|---|---|---|
| Kod jednostki — musi istnieć w Słowniku. Nieznany kod → jednostka odrzucona (raport). |
| X (20) |
| Czy jednostka podstawowa (maks. 1 na wariant). |
| [0-1] |
| Czy jednostka domyślna (maks. 1; musi być aktywna). |
| [0-1] |
| Czy jednostka aktywna. |
| [0-1] |
| Przelicznik względem jednostki podstawowej (dla primary = 1). |
| R2 |
| Rabat (%) dla tej jednostki (≥ 0). |
| [0-9] |
| Minimalna ilość zamówienia w tej jednostce (całkowita > 0). |
| [0-9] |
Przykładowy plik — wariant rodziny
[
{
"family": {
"family_id": "IMPORT-RODZIN-1",
"variant_name": "IMPORT-RODZIN-1-1",
"variant_is_active": "1",
"variant_position": "1",
"col1": { "pl": "Czarny", "en": "Black" },
"col2": { "pl": "Tworzywo sztuczne", "en": "Plastic" },
"col3": { "pl": "M", "en": "M" }
},
"code": "5907595646406",
"additional_code": {
"ean": "5907595646406",
"sku": "Mac Book Air 13,3",
"supplier_product_code": "5907595646406"
},
"price": {
"base_netto": "4000.00",
"purchase_netto": "3300.99",
"srp_netto": "4499.99",
"vat": "23"
},
"manufacturer": { "name": "Apple", "logo": "https://www.apple.com/logo.png" },
"energy_class": {
"energy_class": "A",
"energy_label_link": "https://link.pdf",
"energy_card_information_link": "https://link.pdf"
},
"stock": {
"stock_enabled": "1",
"delivery_date": [
{ "delivery_format": {"pl": "7 dni"}, "delivery_hour": "168", "warehouse_external_id": "1", "restocking_date": "2025-02-02" }
]
},
"package": {
"name": "Euro-pallet",
"dimensions": { "height": "100.00", "width": "100.00", "length": "100.00", "weight": "10.00" }
},
"units": [
{ "external_id": "szt.", "primary_unit": "1", "default_unit": "1", "active": "1", "rate": "1.0", "discount": "0", "minimum_sales_quantity": "1" },
{ "external_id": "opk", "primary_unit": "0", "default_unit": "0", "active": "1", "rate": "10.0", "discount": "10", "minimum_sales_quantity": "1" }
]
}
]
Odpowiedź i raport importu
Import jest nieblokujący — poprawne warianty są importowane, a o problemach z jednostkami dowiesz się z odpowiedzi (batch nie jest odrzucany).
/load_datazwraca liczniki wgranych wierszy orazstock_unit_ignored— listę kodów, które przysłały nieobsługiwane polestock.unit./runzwraca{"status":"ok","report":{...}}, gdziereportzawiera:invalid_units—{product_code, external_id, reason}jednostek odrzuconych (nieznanyexternal_id, nie-całkowite/ujemneminimum_sales_quantity, ujemnyrate/discount, wiele jednostek domyślnych);products_without_primary_fallback_applied— warianty, dla których importer automatycznie ustalił jednostkę podstawową;proc_errors— błędy procedury.
Aktualizacja istniejących produktów
Endpoint /import/products/v2/* tylko dodaje. Aby zmienić dane istniejącego wariantu — stock_enabled, ceny, jednostki sprzedażowe — użyj POST /import/product/update:
Aktualizuje wskazane pola istniejących produktów/wariantów (po
code).Jednostki — tryb przez
units_increase:0= zastąp komplet jednostek;1= aktualizacja częściowa. Pusta tablicaunits→ istniejące jednostki bez zmian.Obowiązują te same reguły integralności jednostek (dokładnie 1 primary, 1 aktywna default).