Pakowanie Kablowo: system magazynowy zbudowany z Claude Code

Case study budowy systemu pakowania dla firmy kablowej. FastAPI + Next.js, integracja BaseLinker, maszyna ESP8266 do kabli, drukarka Zebra. 30% oszczędności czasu.

L

LiveSales

24 min czytania

Setki zamówień dziennie, dwa konta BaseLinker, maszyna do kabli. Jeden system ogarnia wszystko.

“Pakowanie kabli to nie jest standardowy fulfillment. Musisz odmierzyć, nawinąć, oznaczyć. Żaden gotowy WMS tego nie ogarnął. Napisaliśmy swój — i oszczędzamy 30% czasu pakowania dziennie.”
— Właściciel Kablowo, firma kablowa z Allegro/Amazon

Kablowo to firma sprzedająca kable elektryczne, peszle ochronne i akcesoria kablowe na polskich marketplace’ach. Pakowanie kabli jest inne niż pakowanie butów czy elektroniki — trzeba odmierzyć metraż, nawinąć na szpulę lub do pudełka, wydrukować etykietę z kodem MIX, zsynchronizować dwa konta BaseLinker i ogarnąć resztki materiału, które zostają po cięciu.

Żaden gotowy system nie obsługiwał tego workflow. Zbudowaliśmy własny — FastAPI + Next.js + ESP8266 + Zebra printer — i ten artykuł to pełne case study tego procesu.

30% oszczędności czasu

Na pełnej zmianie magazynowej — mierzalne, nie szacunkowe.

2 konta BaseLinker scalone

Pracownik widzi jeden strumień zamówień — system scala w tle.

Maszyna ESP8266

Automatyczne nawijanie i odmierzanie kabla sterowane z aplikacji.


Co sprzedaje Kablowo i dlaczego pakowanie kabli jest inne

Kablowo to firma e-commerce sprzedająca przez Allegro, Amazon PL i Erli. Główne kategorie produktów:

Kable elektryczne

YDYp, YKY, OMY, OWY, przewody instalacyjne. Sprzedawane na metry — klient zamawia 15m, 30m, 100m. Trzeba odmierzyć, nawinąć, oznakować.

Peszle i osłony

Peszle karbowane, rury ochronne, korytkowe. Różne średnice, kolory. Pakowane na sztuki lub metry.

Akcesoria i oleje

Złączki, taśmy izolacyjne, oleje techniczne. Standardowe pakowanie — ale często w zamówieniach mieszanych z kablami.

Dlaczego to nie jest standardowy fulfillment

Firma butów pakuje: weź z półki, włóż do kartonu, naklej etykietę. 60 sekund.

Firma kablowa pakuje:

  1. Sprawdź zamówienie — czy to kabel, peszle, mix?
  2. Odmierz metraż na maszynie (15m kabla YDYp 3x2.5)
  3. Nawiń na szpulę lub włóż do pudełka
  4. Jeśli zamówienie mieszane — zbierz kable + peszle + akcesoria z różnych stref magazynu
  5. Wydrukuj etykietę MIX z kodem grupowym
  6. Sprawdź czy zostały resztki kabla — jeśli tak, zaewidencjonuj

To jest 6 kroków zamiast 3. I każdy wymaga innego narzędzia. Dlatego gotowe WMS-y tego nie obsługują.

Wolumen: setki zamówień dziennie przez dwa oddzielne konta BaseLinker (konto A i B — historyczne powody, dwa osobne sklepy Allegro). Pracownik musi widzieć zamówienia z obu kont jako jeden strumień.


Dlaczego gotowe rozwiązania nie wystarczyły

Problem 1: Dwa konta BaseLinker

BaseLinker nie obsługuje natywnego mergowania zamówień z wielu kont. Pracownik musiał przełączać się między dwoma panelami, co generowało błędy i gubienie zamówień.

Nasze rozwiązanie: Równoległy fetch z obu kont przez asyncio.gather, merge po date_confirmed, zapis do lokalnej bazy z kolumną account (“A”/“B”). Pracownik widzi jeden ekran — system w tle wie, do którego konta odpalić trigger.

Problem 2: Brak widoków pakowania

BaseLinker pokazuje wszystkie zamówienia płasko. Ale w magazynie kablowym masz fizycznie oddzielne strefy: kable (skrętki) leżą gdzie indziej niż peszle, a oleje jeszcze gdzie indziej. Pracownik potrzebuje widoku filtrowanego po strefie produktu.

Nasze rozwiązanie: Cztery widoki pakowania:

  • Skrętki — zamówienia zawierające tylko kable
  • Peszle — zamówienia zawierające tylko peszle/osłony
  • Mix — zamówienia mieszane (kable + peszle + inne)
  • Inne — wszystko co nie pasuje do powyższych

Każdy produkt w katalogu BaseLinker ma custom field strefa (np. SKRETKI, PESZLE, OLIWY). System automatycznie kategoryzuje zamówienia na podstawie stref produktów w nich zawartych.

Problem 3: Integracja z maszyną do kabli

Maszyna do nawijania kabli to ESP8266 (mikrokontroler) sterujący silnikiem. Żaden gotowy WMS nie integruje się z custom hardware. Musieliśmy to zbudować od zera.

Problem 4: Etykiety MIX

Zamówienia mieszane wymagają specjalnej etykiety grupowej (kod MIX) wydrukowanej na drukarce Zebra. Pracownik z jednej strefy pakuje kable, z drugiej peszle — etykieta MIX łączy te paczki w jedno zamówienie.


Architektura systemu

Przepływ danych w systemie Pakowanie Kablowo

BaseLinker API
Konto A + Konto B
FastAPI Backend
Python async + Redis cache
Next.js 16 Frontend
React 19 + WebSocket
PostgreSQL
Zamówienia, produkty, resztki
Redis
Cache 30s zamówienia, 5min produkty
ESP8266
Maszyna do kabli (HTTP)

Stack technologiczny

WarstwaTechnologiaDlaczego
BackendFastAPI (Python 3.12, async)Asynchroniczny I/O idealny do parallel fetch z 2 kont BL
FrontendNext.js 16 (React 19, TypeScript)Server Components + client hydration, shadcn/ui, Tailwind 4
Baza danychPostgreSQL (SQLAlchemy 2.0 async)Alembic migracje, auto-migrate on startup
CacheRedis (graceful fallback)30s TTL zamówienia, 5min produkty, invalidacja po mutacji
Real-timeWebSocketBroadcast order_updated do wszystkich klientów po każdej mutacji
HardwareESP8266 via HTTPPersistent connection pooling, 1s polling, 2s timeout
DrukarkaZebra (ZPL)Print Agent na Windows PC, headless reprint przez API
SkaneryBarcode scanners / telefonyMulti-device picking sync via /api/collected

Scalanie dwóch kont BaseLinker

Kluczowy element architektury: dwa niezależne konta BaseLinker scalane transparentnie.

┌─────────────────┐    asyncio.gather()    ┌─────────────────┐
│  BaseLinker A    │ ──────────────────────>│                 │
│  (konto główne)  │                        │   FastAPI        │
│                  │                        │   merge by       │  ──> Jedna lista
│  BaseLinker B    │ ──────────────────────>│   date_confirmed │      zamówień
│  (konto dodatkowe)│                        │                 │
└─────────────────┘                        └─────────────────┘

Każde zamówienie w bazie ma kolumnę account (“A” lub “B”). Gdy pracownik odpala trigger (np. drukuj spis, drukuj etykietę), system automatycznie wybiera odpowiedniego klienta BaseLinker na podstawie order.account.

Background jobs

Synchronizacja działa w tle bez udziału pracownika:

  • Co 5 minut: odświeżanie zamówień z obu kont BL
  • Co 60 minut: synchronizacja katalogu produktów
  • Raz dziennie: audyt stref (sprawdzenie czy każdy produkt ma przypisaną strefę)

Widoki pakowania — jak pracownik korzysta z systemu

Pracownik magazynu otwiera aplikację na tablecie lub telefonie i widzi cztery zakładki:

Skrętki

Zamówienia zawierające tylko kable. Pracownik przy maszynie do nawijania widzi listę kabli do odmierzenia. Każdy item ma długość w metrach, SKU i strefę.

Peszle

Zamówienia zawierające tylko peszle i osłony. Osobna strefa magazynowa, inny typ pakowania.

Mix

Zamówienia mieszane — kable + peszle + akcesoria. Wymagają etykiety MIX z kodem grupowym. Pracownicy z różnych stref pakują swoje części i łączą je kodem.

Inne

Zamówienia z produktami bez przypisanej strefy lub z kategorii specjalnych. Fallback dla wszystkiego co nie pasuje do 3 głównych widoków.

Flow pakowania krok po kroku

1
Skanowanie kodu kreskowego

Pracownik skanuje kod EAN produktu skanerem Zebra lub telefonem. System automatycznie zaznacza item jako zebrany (picked).

2
Odmierzanie na maszynie ESP

Dla kabli: aplikacja wysyła komendę do ESP8266 z metrażem. Maszyna nawija kabel na szpulę lub do pudełka. Automatyczne odliczanie odpadu.

3
Wybór gabarytu

Pracownik wybiera rozmiar paczki: A (mała), B (średnia), C (duża), 2xC (podwójna). Wpływa na koszt wysyłki.

4
Trigger: spis + etykieta

Pracownik klika “Spis” i “Etykieta” — system odpala makro w BaseLinker (na właściwym koncie A/B). Drukarka Zebra drukuje etykietę.

5
Ewidencja resztek

Jeśli po cięciu został kawałek kabla — pracownik rejestruje resztkę w module resztek. System automatycznie dopasowuje ją do kolejnego zamówienia.


Maszyna do nawijania kabli — ESP8266

To jest serce operacji kablowej. ESP8266 to mikrokontroler Wi-Fi za kilkadziesiąt złotych, który steruje silnikiem DC nawijającym kabel.

Co robi maszyna

  1. Odmierza długość kabla — z dokładnością do 0.1m (encoder na kole)
  2. Nawija na szpulę (tryb szpula) lub wkłada do pudełka (tryb pudełko)
  3. Automatycznie odejmuje odpad — każde cięcie generuje ~0.5m odpadu (nóż + zapas). System to odlicza
  4. Hamuje precyzyjnie — rampa prędkości w dół, żeby kabel nie wyskoczył ze szpuli

Endpointy maszyny

EndpointMetodaCo robi
/api/esp/statusGETStatus maszyny: IDLE, RUNNING, PAUSED, ERROR. Polling co 1s
/api/esp/startPOSTStart cięcia: meters, mode (szpula/pudełko), waste_meters
/api/esp/stopPOSTNatychmiastowe zatrzymanie (emergency stop)
/api/esp/resumePOSTWznowienie po zatrzymaniu (dociąga brakujące metry)
/api/esp/configGET/POSTKonfiguracja: PWM speed, ramp up/down, hamulec, watchdog

Parametry konfiguracji

Maszyna ma kilkanaście parametrów konfiguracyjnych, które admin ustawia przez panel:

  • PWM speed — prędkość obrotowa silnika (0-255)
  • Ramp up — czas rozruchu (ms) — żeby kabel się nie zerwał przy starcie
  • Ramp down — czas hamowania (ms) — precyzyjne zatrzymanie
  • Brake mode — typ hamulca: elektroniczny (PWM reverse) lub mechaniczny
  • Watchdog — auto-stop po X sekundach bez impulsów encodera (zacięcie kabla)
  • Waste offset — stały odpad na cięcie (domyślnie 0.5m)

Persistent connection pooling

ESP8266 to mikrokontroler z ograniczonymi zasobami (80 KB RAM). Każde nowe połączenie TCP to obciążenie. Dlatego nasz backend utrzymuje persistent httpx client z connection poolingiem:

_client = httpx.AsyncClient(
    base_url="http://192.168.1.113",
    timeout=2.0,  # status polling — krótki timeout
    limits=httpx.Limits(max_connections=2, max_keepalive_connections=1),
)

Frontend odpytuje status maszyny co 1 sekundę (hook useESP). Dzięki persistent connection nie ma narzutu TCP handshake przy każdym request.

Dodatkowy debounce: 3 kolejne faile zanim system zadeklaruje maszynę jako offline (unika fałszywych alarmów przy chwilowym zacięciu Wi-Fi).

Wykres wydajności

Wydajność maszyny ESP8266 vs ręczne cięcie kabla

Czas w sekundach na 100 metrów kabla

Ręcznie (100m)
~1,5 min
90 sek
Maszyna ESP (100m)
~20 sek
20 sek
Oszczędność czasu
78%
4.5x szybciej

Kluczowy insight: Maszyna ESP eliminuje etap “odmierzania długości” całkowicie — encoder mierzy metry w trakcie nawijania. Ręcznie trzeba odmierzyć, zaznaczyć, dociągnąć. Z maszyną: wpisujesz 15m, klikasz Start, kabel nawija się sam.


Drukarka Zebra i skanery kodów kreskowych

Etykiety ZPL

System generuje etykiety w formacie ZPL (Zebra Programming Language) — format natywny dla drukarek termicznych Zebra. Etykieta 100x150mm (4x6”) zawiera:

AB7K
kod MIX (4 znaki)

#12345678   POLE-XYZ

Pozycje (3):
2x Kabel YDYp 3x2.5 15m
1x Peszel 32mm 10m
1x Taśma izolacyjna czarna

Kablowo MIX
Kod MIX (4 znaki)

Generowany automatycznie — łączy paczki z różnych stref w jedno zamówienie. Pracownicy szukają siebie nawzajem po tym kodzie.

Numer zamówienia + pole dodatkowe

ID z BaseLinker + opcjonalny kod pola dodatkowego (custom field).

Lista pozycji

Ilość × nazwa produktu. Dynamiczny font — więcej pozycji = mniejsza czcionka.

Drukarka Zebra jest podłączona do PC z Windows. Na PC działa Print Agent — mały serwis HTTP, który:

  • Przyjmuje komendę ZPL z backendu FastAPI
  • Przekazuje ją do drukarki Zebra przez Windows Print API
  • Raportuje status (online/offline, kolejka wydruku)
  • Auto-restart w razie awarii

Endpointy drukowania:

  • GET /api/print/mix-label/{order_id} — renderuje etykietę HTML (otwiera print dialog w przeglądarce)
  • POST /api/print/mix-label/{order_id}/headless — drukuje bezpośrednio na Zebrze (headless, bez przeglądarki)
  • GET /api/print/agent-status — health check agenta
  • GET /api/print/agent-printers — lista dostępnych drukarek

Skanery kodów kreskowych i telefon Zebra

Pracownicy magazynu używają skanerów Zebra (handheld) i telefonów z czytnikiem kodów do pickowania zamówień. System multi-device sync (/api/collected) pozwala na:

  • Skanowanie kodu EAN produktu → automatyczne zaznaczenie jako “zebrane” (picked)
  • Wielourządzeniowy sync — 4 koszyki pickingowe, każdy z własnym skanerem
  • Identyfikacja urządzenia/użytkownika — wiadomo kto co zebrał
  • Auto-cleanup — po spakowaniu zamówienia, stan zebranych itemów się resetuje
Skan → Pick

Skanuj kod kreskowy produktu. System automatycznie zaznacza pozycję w zamówieniu jako zebraną. Real-time update na ekranach wszystkich pracowników.

Telefon Zebra

Urządzenia Zebra z wbudowanym skanerem i ekranem. Pracownik widzi listę do zebrania + skanuje bezpośrednio. Jedno urządzenie na cały flow.


Integracja z BaseLinker API — jakie zapytania wykonujemy

System komunikuje się z BaseLinker przez custom async client z kolejką priorytetową. Oto dokładna lista metod API, z których korzystamy:

Metody API BaseLinker

Metoda APIPriorytetZastosowanieCzęstotliwość
getOrdersNORMALPobieranie zamówień ze statusem “do pakowania”. Paginacja po 100.Co 5 min (cron) + na żądanie
getInventoryProductsListLOWKatalog produktów — nazwy, SKU, EAN, warianty. Paginacja po 100.Co 60 min (cron)
getInventoryProductsDataHIGHSzczegóły produktu: custom fields (strefa, waga, opakowanie zbiorcze), zdjęcia, warianty. Batch po 100 ID.Przy sync zamówień
getInventoryProductsStockHIGHStany magazynowe — ile sztuk/metrów na stanie w danym magazynie.Na żądanie (panel admin)
addInventoryDocumentCRITICALTworzenie dokumentu magazynowego (przyjęcie/wydanie) w trybie draft.Przy operacjach stockowych
addInventoryDocumentItemsCRITICALDodanie pozycji do dokumentu (produkt, ilość, cena).Przy operacjach stockowych
setInventoryDocumentStatusConfirmedCRITICALZatwierdzenie dokumentu — dopiero wtedy zmienia się stan magazynowy.Po dodaniu pozycji
runOrderMacroTriggerCRITICALOdpalenie makra na zamówieniu: “spis” (lista pakowania), “etykieta” (label wysyłkowy).Przy każdym spakowaniu
setOrderStatusCRITICALZmiana statusu zamówienia (np. “do pakowania” → “spakowane”).Przy zmianie statusu
runProductMacroTriggerCRITICALMakro produktowe — aktualizacja danych, zdjęć, cen w katalogu.Na żądanie (admin)
updateInventoryProductsStockCRITICALKorekta stanów magazynowych (set_stock). Używane po mini-inwentaryzacji i operacjach stockowych.Przy korekcie stanu
getInventoryDocumentsNORMALLista dokumentów magazynowych (przyjęcia, wydania, korekty) z filtrami po typie i dacie.Panel admin

System priorytetów i rate limiting

Nasz BaseLinker client to nie jest zwykły wrapper na requests.post(). To asynchroniczny klient z kolejką priorytetową:

CRITICAL (priorytet 0)
Triggery, operacje stockowe, zmiana statusu. Bez limitu kolejki.
HIGH (priorytet 1)
Odczyty użytkownika (szczegóły produktu). Max 50 w kolejce.
NORMAL (priorytet 2)
Zaplanowane synchronizacje (cron). Max 20 w kolejce.
LOW (priorytet 3)
Bulk sync katalogu. Max 100 w kolejce.
Rate limiter

30 zapytań / 60 sekund (sliding window) per konto. BaseLinker zwraca HTTP 429 przy przekroczeniu — nasz client automatycznie czeka i retry z exponential backoff (1s → 2s → 4s, max 3 próby).

Auto-retry

Retry tylko dla 429 (rate limit) i 5xx (server error). Błędy logiczne (400, 404) nigdy nie są retryowane — to by maskowało bugi.

Timeout

60 sekund na jedno zapytanie. BaseLinker bywa wolny przy dużych batchach (500+ zamówień) — krótszy timeout powodował fałszywe faile.

Dynamic trigger engine

Trigger ID nie są hardcoded w kodzie. Są w zmiennej środowiskowej jako JSON:

ACCOUNT_A_TRIGGERS={"skretki.spis":44364, "skretki.etykieta":44365, "mix.spis":44366, "mix.etykieta":44367}

GET /api/triggers/available skanuje JSON dict po prefixie widoku. Dodanie nowego triggera = edycja zmiennej + restart. Zero zmian w kodzie.


Wykres oszczędności czasu pakowania

Czas pakowania 20 zamówień: ręcznie vs z aplikacją

Porównanie w minutach — realne dane z magazynu Kablowo

Ręcznie
3h 10min
Z aplikacją
1h 56min
Oszczędność
39%

Co dokładnie oszczędzamy

Skrętki (kable): Największa oszczędność — maszyna ESP eliminuje ręczne odmierzanie. Zamiast mierzyć, zaznaczać, ciągnąć kabel — pracownik wpisuje metraż i klika Start.

Mix (zamówienia mieszane): Etykiety MIX z automatycznym kodem grupowym eliminują szukanie siebie nawzajem po magazynie. Pracownik z jednej strefy widzi kod “AB7K”, pracownik z drugiej strefy szuka tego samego kodu — i łączą paczki.

Picking (zbieranie): Skanery kodów kreskowych eliminują ręczne sprawdzanie listy. Skan → pick → następny. Bez czytania nazw i porównywania.

Etykietowanie Zebra: Headless printing eliminuje przełączanie okien, szukanie drukarki, formatowanie. Klik → etykieta na Zebrze. 5 sekund.

Łącznie: ~39% oszczędności czasu na pełnej zmianie pakowania (20 zamówień per batch). Na skali miesiąca to kilkadziesiąt godzin roboczych.


Zdjęcia i dokumentacja pakowania

System posiada pełny moduł mediów do dokumentacji operacji magazynowych:

Zdjęcia w checklistach

Każdy krok checklisty może wymagać zdjęcia jako dowodu wykonania. Max 10 MB per zdjęcie.

Video dokumentacja

Nagrania wideo procesu pakowania lub odbioru transportu. Max 50 MB per video.

Media transportowe

Zdjęcia załadunków, pojazdów, dowodów dostawy. Powiązane z konkretnymi kursami.

Pliki organizowane są automatycznie: YYYY/MM/DD/{uuid}-{oryginalna_nazwa}. UUID zapewnia unikalność, oryginalna nazwa pozwala na identyfikację. Każdy plik ma tracking: kto uploadował, kiedy, jaki typ MIME.


Struktura wewnętrznego API

Cały backend to FastAPI z czystym podziałem na warstwy: Router (HTTP) → Service (logika) → Repository (baza danych).

Drzewo endpointów

/api
GET /health
/auth
POST /login, GET /users, POST /users
/orders
GET / (lista), GET /counts, POST /sync
POST /{id}/pick-item, /gabaryt, /report-error, /transfer-to-admin
/products
GET /, /stock   POST /stock, /consumption
/triggers
GET /available   POST /order, /product
/leftovers
GET /, /search-products, /match, /matches
POST /, /{id}/reserve, /release, /consume, /use
/collected
GET /   POST /toggle   DELETE /by-order/{id}
/checklists
GET /templates, /today, /history
POST /templates, /instances/{id}/start, /complete-step, /complete
/warehouse
GET /layout, /stats, /rack-types, /racks, /shelves
POST/PUT CRUD regałów, półek, stref, lokalizacji produktów
POST /quiz/start, /quiz/answer
/esp
GET /status, /config   POST /start, /stop, /resume, /config
/print
GET /mix-label/{id}, /agent-status, /agent-printers
POST /mix-label/{id}/headless
/transport
GET /routes, /trips/history, /vehicles, /fuel-prices
POST /trips/{group}/depart, /arrive
/admin
GET /dashboard, /error-reports, /carrier-config, /strefa-audit
POST/PUT /resolve, /dismiss, /pack, /unpack, /carrier-override
/ws/orders
WebSocket real-time updates (order_updated, checklist_updated, print_mix_label)

Format odpowiedzi

Każdy endpoint zwraca ustandaryzowany JSON:

{
  "status": "SUCCESS",
  "data": { ... },
  "count": 42
}

Lub w przypadku błędu:

{
  "status": "ERROR",
  "detail": "Order not found",
  "error_code": "ERR_NOT_FOUND"
}

Pełna dokumentacja Swagger

Wygenerowaliśmy pełną dokumentację API w formacie OpenAPI 3.0, dostępną jako interaktywny Swagger UI: Dokumentacja API Pakowanie Kablowo.

Swagger zawiera wszystkie endpointy, schematy request/response, kody błędów i przykłady. Dokumentacja jest standalone — nie jest podpięta do produkcyjnego backendu.


Podsumowanie

Kluczowe metryki

~39%
oszczędność czasu pakowania
2
konta BL scalone
0
double-fires triggerów
1s
real-time sync (WebSocket)

Powiązane artykuły

Ten system to nie tylko jeden monolit — składa się z kilku wyspecjalizowanych modułów. Każdy z nich ma swój osobny artykuł:

Cały system zbudowaliśmy z Claude Code w kilka tygodni. ROI zwrócił się w pierwszym miesiącu — głównie dzięki oszczędności czasu pakowania i redukcji strat materiałowych na resztkach kablowych.

Zainteresowany automatyzacją danych?

LiveSales pomoże Ci zaoszczędzić czas i podejmować lepsze decyzje biznesowe dzięki automatycznym raportom i dashboardom.

Skontaktuj się z nami

Podobał Ci się ten artykuł?

Subskrybuj, aby dostawać powiadomienia o nowych artykułach.

Bez spamu. Możesz się wypisać w każdej chwili.