W tym artykule
Co z tego wyszło
Hurtownia pasz i akcesoriów dla zwierząt sprzedaje przez Allegro i własny sklep, a zamówienia zbiera w Apilo. Towar to worki od 2 do 25 kg, więc prawie każda paczka ociera się o limit przewoźnika. Zbudowaliśmy nadbudowę nad Apilo, która sama dzieli zamówienie na paczki, sama dobiera gabaryt skrytki, drukuje etykiety całą partią — i uczy się od operatorki za każdym razem, gdy ta poprawi jej propozycję.
- 1
Podział na paczki liczy jeden algorytm
Podgląd pojedynczego zamówienia i druk zbiorczy wołają tę samą funkcję. Wcześniej druk zbiorczy szedł własną regułą „1 sztuka = 1 paczka” i kupował więcej przesyłek, niż trzeba było.
- 2
Limity przewoźników w konfiguracji, nie w kodzie
Waga i wymiary per kategoria (paczkomat, punkt odbioru, kurier) siedzą w jednym pliku JSON. Nowy przewoźnik albo zmiana limitu to wpis, nie zmiana logiki.
- 3
System uczy się od operatorki
Ręczna korekta podziału zapisuje się jako wzór dla identycznego zestawu pozycji. Kolejne takie zamówienie pakuje się samo — również w druku zbiorczym.
- 4
Nic nie idzie do druku bez dowodu
Przed partią panel sprawdza, które zamówienia mają już etykietę w Apilo. Każda utworzona przesyłka jest płatna u kuriera i nieodwracalna z poziomu integracji.
Poniedziałek: 80 etykiet i worki paszy
Zamówienia z Allegro i ze sklepu spływają do Apilo. Apilo dobrze zbiera je w jednym miejscu i potrafi utworzyć przesyłkę u kuriera, ale wciąż jest to praca na jednym zamówieniu naraz: otwórz, sprawdź, co w środku, zdecyduj, ile paczek, wpisz wagę, wybierz gabaryt, utwórz przesyłkę, wydrukuj. Przy dwudziestu zamówieniach da się to przeżyć. W poniedziałek rano bywa ich osiemdziesiąt.
Drugi problem to asortyment. Sklep z paszami nie sprzedaje pudełek 20×15×10 cm. Sprzedaje worki 10, 20 i 25 kg — towar, który przy każdym zamówieniu ociera się o limit wagowy albo wymiarowy przewoźnika. Ta sama pozycja raz mieści się w paczkomacie, a raz musi jechać kurierem.
Trzeci problem jest najdroższy: każda przesyłka utworzona w Apilo jest płatna u kuriera i nie da się jej cofnąć z poziomu integracji. Pomyłka nie kończy się komunikatem o błędzie, tylko fakturą. Dlatego cała ta automatyzacja jest zbudowana wokół pytania „czy na pewno wolno to utworzyć”, a nie wokół „jak zrobić to szybciej”.
Dlaczego „podziel na paczki” to nie jest suma wag
Pierwsza intuicja — zsumuj wagi pozycji i podziel przez limit przewoźnika — wywraca się na trzech rzeczach naraz:
- Paczkomat ma gabaryty, nie tylko wagę. Skrytka A ma 8 cm wysokości, B — 19 cm, C — 41 cm. Worek 10 kg jest lekki w stosunku do limitu 25 kg, ale jest pękaty i do gabarytu B nie wejdzie.
- Ta sama zawartość, inny przewoźnik, inny podział. Dwa worki po 10 kg mieszczą się w jednej przesyłce paczkomatowej (20 ≤ 25 kg), w punkcie odbioru z limitem 20 kg są dokładnie na granicy, a u kuriera (31,5 kg) można dorzucić trzeci.
- Podział przekłada się wprost na koszt. Każda dodatkowa paczka to dodatkowa, płatna przesyłka. Reguła „jedna sztuka = jedna paczka” jest bezpieczna i systematycznie przepłaca.
Dlatego podział liczy algorytm pakowania (first-fit decreasing) z limitem wagowym kategorii przewoźnika, a gabaryt wybiera najbardziej wymagająca sztuka w paczce, a nie suma wag. Worek od 10 kg w górę wymusza gabaryt C, nawet jeśli waga zmieściłaby się w mniejszej skrytce.
Skąd system w ogóle wie, ile waży worek
Apilo zwraca pozycje zamówienia z nazwą, SKU, ilością i ceną. Nie zwraca wagi — najczęściej po prostu nie ma jej w kartotece sprzedażowej. Ratuje nas branża: waga jest w nazwie produktu („Pasza dla koni 25kg”, „Granulat dla drobiu 10kg”). Wagę czyta więc parser nazwy — i to jest dokładnie ten kawałek kodu, który potrafi po cichu kosztować pieniądze.
„Worek 22,5 kg" → regex z samą kropką → 5 kg ❌ zaniżona deklaracja → dopłata od kuriera „Mieszanka 1,25kg" → regex z samą kropką → 25 kg ❌ fałszywa blokada limitu „Worek 22,5 kg" → regex z przecinkiem → 22,5 kg ✅
Wyrażenie regularne, które uznaje tylko kropkę dziesiętną, z „Worek 22,5 kg” robi 5 kg, a z „Mieszanka 1,25kg” — 25 kg. Pierwszy błąd to zaniżona deklaracja i dopłata od przewoźnika po fakcie; drugi to fałszywa blokada limitu na czymś, co waży kilogram. Oba wyglądają w panelu zupełnie normalnie.
Limity przewoźników: waga to połowa problemu
Pierwsza wersja wysyłała do Apilo sztywne wymiary 60×40×40 cm dla każdej przesyłki. U kuriera przechodziło. Aż do pierwszej Orlen Paczki:
422 DIMENSIONS_VALIDATION_ERROR packages.dimensions Podane wymiary wykraczają poza limit 64 x 41 x 38 cm
Wysokość 40 cm przy limicie 38 cm wywracała całą przesyłkę, a komunikat wracał do operatorki jako surowy błąd 422. Naprawa polegała na przeniesieniu limitów z kodu do konfiguracji — jeden plik carrier-limits.json trzyma i wagę, i wymiary per kategoria przewoźnika:
{
"id": "inpost_paczkomat", "label": "InPost Paczkomat",
"match": ["paczkomat"], "maxKg": 25,
"maxDims": { "l": 64, "w": 38, "h": 41 },
"defaultDims": { "l": 60, "w": 35, "h": 38 }
},
{
"id": "locker_or_point", "label": "Automat / punkt odbioru",
"match": ["automat", "punkt", "orlen paczka", "żabka"], "maxKg": 20,
"maxDims": { "l": 64, "w": 41, "h": 38 },
"defaultDims": { "l": 60, "w": 40, "h": 30 }
}Kategorię rozpoznajemy po czytelnej nazwie metody wysyłki („ORLEN Paczka — odbiór w punkcie”), a nie po kodzie metody — kody bywają identyfikatorami platformy sprzedażowej i nie da się ich dopasować do reguł. Dodanie przewoźnika albo zmiana limitu to dziś edycja JSON-a; domyślne wymiary są przycinane do limitu, żeby zamiast błędu 422 poszła mniejsza paczka.
Jeden mózg pakowania — i nauka z druku zbiorczego
Najgorsza wersja takiego systemu to ta, w której podgląd pokazuje co innego niż druk. Mieliśmy to przez chwilę: podgląd zamówienia liczył paczki algorytmem, a druk zbiorczy szedł regułą „1 sztuka = 1 paczka”. Operatorka widziała jedną paczkę, a partia kupowała dwie przesyłki. Dziś obie ścieżki wołają tę samą funkcję, a kolejność źródeł decyzji jest jawna:
- 1
Ręczna korekta z tego zamówienia
Jeśli operatorka poprzestawiała pozycje w podglądzie, jej podział wygrywa ze wszystkim innym.
- 2
Zapamiętany wzór
Odcisk zamówienia to kategoria przewoźnika plus zestaw SKU z ilościami. Trafienie znaczy, że dokładnie taki komplet ktoś już kiedyś spakował ręcznie.
- 3
Algorytm pakowania
Dopiero gdy nie ma ani korekty, ani wzoru, liczy first-fit decreasing z limitem wagowym kategorii i regułą gabarytu.
Wzór jest odciskiem całego zamówienia, nie pojedynczej pozycji: kategoria_kuriera::sku:ilość, posortowane. Inna ilość tego samego SKU to inny wzór, bo inaczej się go pakuje. Inny typ przewoźnika też, bo ma inny limit.
Druk zbiorczy też uczy się — a to była większa zmiana, niż wygląda. Wcześniej wzory powstawały wyłącznie w podglądzie pojedynczego zamówienia, czyli w ścieżce, którą operatorka chodzi najrzadziej. Cały główny ruch szedł partiami i nie uczył systemu niczego.
Partia druku: koszyk, pre-flight, przegląd wag
Zamówienia trafiają do koszyka zapisanego w bazie, a nie w zakładce przeglądarki. Dzięki temu partię można zacząć kompletować przed południem, wrócić do niej po obiedzie i zobaczyć na liście, że koleżanka wrzuciła te same zamówienia do swojego koszyka.
Zanim powstanie choćby jedna przesyłka, panel odpowiada na pytanie „które z tych zamówień ma już etykietę w Apilo”. Dowód zbiera z trzech źródeł:
- lustro przesyłek w naszej bazie, odświeżane synchronizacją godzinową,
- status zamówienia w Apilo („Wysłane”, „Doręczone”) — jest na liście, więc nie kosztuje ani jednego dodatkowego zapytania,
- tag „Etykieta utworzona” — najpewniejszy, ale wymaga osobnego zapytania na każde zamówienie, więc pytamy o niego punktowo.
Werdykt jest trójstanowy: ma etykietę, nie ma, albo nie wiadomo. Zwinięcie „nie wiadomo” do „nie ma” kosztuje jedną płatną przesyłkę na każde nierozstrzygnięte zamówienie — dlatego takie zamówienia po prostu wypadają z partii z czytelnym komunikatem. Z tego samego powodu koszyk ma dwie różne akcje: „Generuj etykiety” (tworzy, płatne, pomija te z etykietą) i „Drukuj wszystkie etykiety” (czysty odczyt, wolno klikać do skutku).
Ostatni krok przed nadaniem to przegląd wag. Produkty są deduplikowane po SKU, więc operatorka poprawia wagę raz na towar, a nie raz na zamówienie; brak wagi blokuje przycisk, a waga ponad limit kategorii dostaje ostrzeżenie, zanim odrzuci ją Apilo. Partia jest ograniczona do dwudziestu zamówień — powyżej tego równoległe wywołania API przestają być grzeczne.
Zamówienie anulowane po wydrukowaniu etykiety
Synchronizacja z Apilo chodzi co godzinę. Operatorka kompletuje partię w kilkanaście minut. To wystarczy, żeby zamówienie anulowane o 10:05 dostało płatną etykietę o 10:20 — i żeby nikt nie zauważył tego aż do rozliczenia z kurierem.
Dlatego anulowanie nie czeka na synchronizację, tylko przychodzi z Apilo natychmiast, regułą automatyzacji wywołującą nasz adres URL. Zapis trafia do osobnej tabeli, a strażnik po stronie serwera odrzuca próbę utworzenia przesyłki dla anulowanego zamówienia. To jedyna blokada, której nie da się ominąć przyciskiem „generuj mimo to”: drugą etykietę operatorka czasem chce świadomie, etykiety dla anulowanego — nigdy.
Co to zmieniło (i czego nie rozwiązuje)
Codzienna praca wygląda dziś tak:
- Kompletowanie partii zamiast klikania zamówień. Filtr po SKU i przewoźniku, koszyk, jeden przegląd wag, jeden zbiorczy PDF do drukarki.
- Podział na paczki jest decyzją systemu, a gdy operatorka go poprawi, kolejne identyczne zamówienie jest już spakowane dobrze.
- Limity przewoźnika widać przed drukiem, a nie w postaci błędu 422 z Apilo po fakcie.
- Anulowane i już oetykietowane zamówienia wypadają z partii same.
Nie podaję tu procentów oszczędności, bo uczciwa liczba bierze się z pomiaru, a nie z prezentacji. System zbiera własną telemetrię: ile podziałów przyjęto bez zmian, ile poprawiono, skąd wzięła się podpowiedź (zapamiętany wzór czy algorytm) i jak często waga jest nieznana. Zakładka Trafność AI pokazuje to jako serię w czasie — i to jest jedyna wersja zwrotu z inwestycji, którą warto pokazywać klientowi.
Wnioski dla twojego magazynu
- 1
Zacznij od tego, co kosztuje pieniądze
W wysyłce najdroższy nie jest czas kliknięcia, tylko nadmiarowa przesyłka, dopłata za zaniżoną wagę i etykieta dla anulowanego zamówienia. Automatyzuj wokół tych trzech, a nie wokół „szybciej”.
- 2
Jedna ścieżka decyzji, nie dwie
Jeśli podgląd i wykonanie liczą to samo dwoma kawałkami kodu, prędzej czy później rozjadą się w sposób, który pierwszy zobaczy klient.
- 3
Limity i reguły do konfiguracji
Tabela limitów przewoźników zmienia się częściej niż logika. Trzymana w kodzie, starzeje się od razu w dwóch kopiach.
- 4
Pomiar od pierwszego dnia
Telemetria „ile system zrobił sam, a ile poprawił człowiek” kosztuje kilka linijek przy każdej operacji, a po miesiącu jest jedyną twardą odpowiedzią na pytanie, czy to się opłaciło.
Jeśli interesuje cię strona techniczna — pułapki samego API Apilo: tokeny, cichy limit 512 rekordów, metody przewoźników, tagi i webhooki — opisałem je w osobnym wpisie.
Mateusz Kozłowski
Założyciel flowbiz · Ekspert automatyzacji procesów
Wdrażam automatyzacje, integracje i AI w średnich firmach na Pomorzu i w Kujawsko-Pomorskiem.
