Informatyczny system magazynowy jako układ warstw

Informatyczny system magazynowy składa się z warstw, które różnią się zadaniem i sposobem awarii. Baza danych przechowuje stan i historię, serwer aplikacji wykonuje reguły procesu, a terminale przenoszą do systemu fizyczne zdarzenia z hali. Osobną warstwę tworzą integracje, czyli kanały wymiany danych z ERP i sklepem internetowym.

Podział ma znaczenie praktyczne. Wolne skanowanie na hali może mieć przyczynę w zasięgu sieci, w zapytaniu bez indeksu albo w zbyt małej puli połączeń, a bez rozdzielenia warstw diagnoza sprowadza się do zgadywania. Zawartość samych tabel i dokumentów opisuje osobny tekst o magazynowym systemie informatycznym i jego modelu danych; tutaj skupiamy się na tym, co leży wokół bazy.

WarstwaZadanieTypowe technologieObjaw awarii
Prezentacjaekrany operatora i aplikacja mobilnaprzeglądarka, JavaScript, jQuery, aplikacja Androidwolne odświeżanie widoku
Logikareguły procesu, walidacja, uprawnieniaserwer aplikacji na C#, IISbłędy 500, przekroczenie czasu żądania
Daneprzechowywanie stanu i historiiSQL Server, T-SQL, procedury składowaneblokady, długie zapytania
Integracjawymiana z ERP i sklepemREST, pliki XML, EDIrozbieżność stanów między systemami
Urządzeniaskanowanie i druk etykietterminale Android, drukarki ZPLutrata łączności, błędny odczyt

Reguła: reguły procesu leżą w warstwie logiki i bazy, a nie w aplikacji na terminalu, która zna tylko bieżący ekran.

Droga jednego skanu przez warstwy

Skan kodu przechodzi przez wszystkie warstwy w ułamku sekundy, a każdy etap może go odrzucić. Znajomość tej ścieżki pozwala odczytać komunikat błędu i wskazać, gdzie szukać przyczyny.

  • Urządzenie - skaner dekoduje kod i przekazuje wartość do aplikacji, która dodaje identyfikator operacji.
  • Serwer aplikacji - sprawdza uprawnienia operatora, poprawność formatu kodu oraz status dokumentu.
  • Baza danych - procedura znajduje towar po kodzie EAN, księguje ruch w transakcji i zwraca wynik albo błąd biznesowy.
  • Odpowiedź - aplikacja pokazuje komunikat operatorowi, a przy braku odpowiedzi ponawia wysyłkę z tym samym identyfikatorem.

Warstwa danych i serwer SQL Server

Baza jest jedynym miejscem, w którym stan magazynu istnieje w jednej wersji. Wszystko inne, od widoku w przeglądarce po komunikat do ERP, jest jej odbiciem. Studio WMS.net działa na bazie MS SQL Server, a szerszy opis takiego układu daje strona o systemie magazynowym opartym na SQL.

Pliki danych i dziennik transakcji

SQL Server zapisuje każdą zmianę najpierw w dzienniku transakcji, a dopiero później w pliku danych. Dziennik ma charakter sekwencyjny, więc jego opóźnienie zapisu bezpośrednio wydłuża każde księgowanie dokumentu. Z tego powodu pliki danych i dziennika umieszcza się na osobnych wolumenach, a dziennik na najszybszym z nich.

Ilość zapisów zależy od liczby skanów, ponieważ każdy odczyt kodu na terminalu kończy się transakcją. Magazyn kompletujący kilka tysięcy linii na zmianę generuje więc zapis o innym profilu niż magazyn z kilkudziesięcioma przyjęciami dziennie. Wielkość bazy należy szacować z uwzględnieniem tabeli ruchów, która rośnie liniowo z liczbą operacji.

Model odzyskiwania i kopie zapasowe

Ustawienie modelu odzyskiwania decyduje o tym, ile transakcji można utracić po awarii. Dla magazynu, w którym dokumenty są jedynym śladem operacji, sensowny jest model FULL z regularnymi kopiami dziennika.

Model odzyskiwaniaKopie dziennikaOdtworzenie do punktu w czasieZastosowanie
SIMPLEbrak, dziennik jest cyklicznie zwalnianytylko do ostatniej kopii pełnejśrodowiska testowe
FULLwymagane, w regularnych odstępachdo dowolnej chwili z zakresu kopiiprodukcyjny magazyn
BULK_LOGGEDwymaganeograniczone dla operacji masowychkrótkie okna importu

Przykład poniżej wykonuje kopię dziennika transakcji do pliku. W środowisku produkcyjnym polecenie uruchamia zadanie SQL Agent co kilkanaście minut, a nazwa bazy jest umowna. Pełny opis składni zawiera dokumentacja BACKUP, a modele odzyskiwania omawia osobna strona Microsoft Learn.

BACKUP LOG MagazynDB
    TO DISK = N'E:\kopie\MagazynDB_log.trn'
    WITH COMPRESSION, CHECKSUM, INIT;

Indeksy i ich koszt przy księgowaniu

Każdy indeks przyspiesza odczyt, ale spowalnia zapis, bo silnik aktualizuje go przy każdej zmianie wiersza. W tabeli ruchów, do której dopisuje się przy każdym skanie, nadmiar indeksów bezpośrednio wydłuża księgowanie. Zapytanie poniżej wyświetla indeksy o największej liczbie aktualizacji wraz z liczbą odczytów; indeks aktualizowany tysiące razy i czytany kilka razy jest kandydatem do usunięcia. Widok opisuje dokumentacja sys.dm_db_index_usage_stats.

SELECT TOP (10)
       OBJECT_NAME(i.object_id) AS Tabela,
       i.name                   AS Indeks,
       s.user_seeks, s.user_scans, s.user_updates
FROM sys.indexes AS i
JOIN sys.dm_db_index_usage_stats AS s
  ON s.object_id = i.object_id
 AND s.index_id  = i.index_id
 AND s.database_id = DB_ID()
ORDER BY s.user_updates DESC;

Serwer aplikacji i komunikacja asynchroniczna

Serwer aplikacji przyjmuje żądania z przeglądarek i terminali. Sprawdza uprawnienia i wywołuje procedury w bazie. Zwykle działa na Windows Server pod kontrolą IIS. Ta warstwa musi być bezstanowa: żaden ekran nie może zakładać, że ten sam proces obsłuży następne żądanie.

Reguła: stan operacji nie zostaje w pamięci procesu, bo kolejne żądanie może trafić do innego procesu albo innego serwera.

Po stronie przeglądarki wywołanie wysyła się asynchronicznie, żeby okno nie zamarzało na czas księgowania. Skrypt jQuery poniżej przekazuje odczyt kodu do serwera i ustawia krótki limit czasu. Adres i nazwy pól są przykładowe.

$.ajax({
    url: '/api/przyjecie/skan',
    method: 'POST',
    contentType: 'application/json',
    data: JSON.stringify({ dokumentId: 1024, kod: '5901234123457', ilosc: 1 }),
    timeout: 5000
})
.done(function (odp) { $('#status').text(odp.komunikat); })
.fail(function (xhr, status) {
    $('#status').text(status === 'timeout' ? 'Brak odpowiedzi serwera' : 'Błąd zapisu');
});

Po stronie serwera metoda w C# wywołuje procedurę składowaną z limitem czasu polecenia krótszym niż limit klienta. Dzięki temu długo blokowana transakcja kończy się błędem w bazie, a nie zawieszeniem wielu wątków serwera.

using var cn = new SqlConnection(_connectionString);
await cn.OpenAsync();
using var cmd = new SqlCommand("dbo.KsiegujPrzyjecie", cn)
{
    CommandType = CommandType.StoredProcedure,
    CommandTimeout = 4
};
cmd.Parameters.Add("@DokumentId", SqlDbType.Int).Value = dto.DokumentId;
cmd.Parameters.Add("@Kod", SqlDbType.NVarChar, 50).Value = dto.Kod;
cmd.Parameters.Add("@Ilosc", SqlDbType.Decimal).Value = dto.Ilosc;
await cmd.ExecuteNonQueryAsync();
  • Pula połączeń - liczba równoległych połączeń do bazy ogranicza przepustowość, więc zbyt mała pula tworzy kolejkę w aplikacji, a zbyt duża przeciąża serwer SQL.
  • Limity czasu - limit polecenia w bazie powinien być krótszy niż limit żądania HTTP, a ten krótszy niż limit w aplikacji mobilnej.

Operacje długie, na przykład generowanie raportu czy przeliczenie stanów, nie powinny blokować żądania. Serwer zapisuje zlecenie w tabeli zadań i zwraca identyfikator, a przetwarza je proces w tle, na przykład zadanie SQL Agent albo usługa Windows. Klient sprawdza status po identyfikatorze co kilka sekund. Ten sam wzorzec chroni przed podwójnym wysłaniem: identyfikator operacji generuje terminal, a serwer odrzuca powtórzony.

Terminale i drukarki etykiet

Terminal mobilny z Androidem ma wbudowany skaner i moduł Wi-Fi, a operator obsługuje go przez ekran dotykowy. Aplikacja magazynowa dla takiego terminala, na przykład opisana w tekście o aplikacji magazynowej na Androida, nie przechowuje reguł procesu, tylko odczytuje kod i pokazuje wynik operacji. Starsze wdrożenia z terminalami radiowymi omawia strona o terminalu radiowym w magazynie, a przegląd urządzeń zawiera serwis terminali radiowych SoftwareStudio.

Skanery kodów kreskowych i kolektory danych używane do obsługi operacji w systemie magazynowym
Skanery i kolektory danych jako warstwa urządzeń - źródło zdarzeń, które serwer aplikacji przekształca w ruchy

Zasięg sieci Wi-Fi jest ograniczeniem częściej niż wydajność serwera. Terminal przechodzący między punktami dostępowymi na korytarzu regałów traci na moment połączenie, a aplikacja musi zachować odczyt i ponowić wysłanie. Platforma Android udostępnia do tego mechanizmy opisane w dokumentacji łączności Wi-Fi.

UrządzenieInterfejsWymaganie dla systemu
Terminal AndroidWi-Fi, HTTP do serwera aplikacjiponawianie wysyłki po utracie zasięgu
Skaner w obudowie terminalaemulacja klawiatury lub intentrozpoznawanie kodów EAN oraz GS1-128
Drukarka etykietsieć, protokół ZPLszablon etykiety zależny od typu jednostki
Stacja stacjonarnaprzeglądarkaobsługa okien z pełną listą pozycji

Drukarki termiczne przyjmują etykiety jako polecenia w języku ZPL wysyłane przez sieć. Fragment poniżej drukuje kod Code 128 z numerem w opisie; wymiary i pozycja zależą od rozdzielczości głowicy.

^XA
^FO50,50
^BCN,100,Y,N,N
^FD5901234123457^FS
^XZ

Nowa wersja aplikacji mobilnej trafia na terminale stopniowo, więc serwer obsługuje jednocześnie starą i nową. Adresy interfejsu opatruje się numerem wersji, na przykład w ścieżce, a zmiany łamiące kompatybilność wprowadza się w nowej wersji punktu końcowego. Aktualizacje floty urządzeń realizuje się przez system zarządzania urządzeniami, a nie ręcznie.

Integracja z ERP i sklepem internetowym

System magazynowy rzadko działa samodzielnie. Zlecenia przychodzą z ERP albo ze sklepu, a magazyn zwraca potwierdzenia wykonania oraz aktualne stany. Wybór sposobu wymiany zależy od tego, jak szybko druga strona musi widzieć zmianę i co się stanie, gdy łączność zniknie.

W wymianie EDI zlecenie przyjęcia przychodzi jako wiadomość ORDERS albo awizo dostawy DESADV, a potwierdzenie przyjęcia wraca jako RECADV. Adapter tłumaczy komunikat na dokument w tabelach pośrednich i sprawdza numery GTIN oraz SSCC. Błędnej wiadomości nie odrzuca w ciszy, tylko zapisuje ją z opisem przyczyny w tabeli błędów, którą monitoruje administrator.

Sposób wymianyOpóźnienieOdporność na przerwę łącznościTypowe użycie
Pliki XML lub CSV w kataloguminutywysoka, plik czeka na odbiórstarsze ERP, wymiana partiami
Tabele pośrednie w baziesekundywysoka, wiersze czekają na przetworzenieERP na tym samym serwerze SQL
REST API z kolejkąsekundyzależy od kolejki i ponowieńsklep internetowy, systemy w chmurze
EDI (EANCOM)minutywysoka, potwierdzenia wiadomościwymiana z dostawcami i sieciami handlowymi

W integracji ze sklepem internetowym najczęstszym źródłem błędów jest niezgodność identyfikatorów. Sklep operuje kodem SKU, a magazyn kodem EAN lub indeksem, więc potrzebna jest tabela mapowań utrzymywana w jednym miejscu. Przykład sklepu jako źródła zamówień opisuje tekst o systemie WMS dla sklepu internetowego.

Ilustracja integracji systemów magazynowych z ERP i innymi aplikacjami przez interfejsy wymiany danych
Integracje jako osobna warstwa - kanały, którymi zlecenia trafiają do magazynu, a potwierdzenia wracają do ERP

Drugą pułapką jest to, która liczba trafia do sklepu. Stan fizyczny w lokalizacjach nie równa się ilości dostępnej do sprzedaży, bo część towaru jest zarezerwowana pod zlecenia. Sklep powinien otrzymywać stan dostępny, liczony osobno, a zasady jego wyznaczania opisuje strona o zarządzaniu stanami magazynowymi. Częstotliwość aktualizacji dobiera się do tempa sprzedaży: przy szybko schodzącym towarze zmiana stanu wysyłana jest od razu, a dla pozostałych zbiorczo co kilka minut.

Wiadomość ze stanami przychodzi zwykle jako JSON i trafia do tabeli pośredniej, skąd procedura przenosi ją do właściwych tabel. Funkcja OPENJSON rozbija dokument na wiersze, co opisuje dokumentacja OPENJSON.

INSERT dbo.StanyImport (Sku, Dostepne)
SELECT Sku, Dostepne
FROM OPENJSON(@json)
     WITH (Sku      NVARCHAR(50) '$.sku',
           Dostepne INT          '$.dostepne');

Przy większej liczbie systemów źródłowych sam import przestaje wystarczać. Wtedy potrzebna jest osobna warstwa pośrednia, którą opisuje strona o integracji systemów magazynowych, a przykład wymiany z dużym ERP omawia tekst o integracji WMS z SAP. Zasady łączenia aplikacji przez API zawiera strona o integrowaniu aplikacji webowych.

Hosting i bezpieczeństwo systemu

Ta sama architektura działa na serwerach firmy albo w chmurze, ale obie opcje inaczej rozkładają obowiązki. Przy własnym serwerze firma odpowiada za sprzęt i utrzymanie: kopie oraz aktualizacje. W chmurze część tych zadań przejmuje dostawca, a zmienia się model kosztów i zależność od łącza. Porównanie kosztów i skalowania zawiera tekst o programie magazynowym w chmurze, a układ na własnej infrastrukturze opisuje strona o systemie do magazynu na serwerach firmy. Ogólne zestawienie znajduje się też w artykule o WMS w chmurze i on-premise.

Środowiska i aktualizacje

Aktualizacja systemu magazynowego zmienia procedury i schemat bazy, więc przed wdrożeniem sprawdza się ją w środowisku testowym z kopią danych produkcyjnych. Dane osobowe w takiej kopii należy zanonimizować. Test obejmuje pełny cykl od przyjęcia do wydania oraz wymianę z ERP, bo integracja często zawodzi po zmianie formatu pola.

Wysoka dostępność bazy

Awaria pojedynczego serwera zatrzymuje wydania, więc przy większym magazynie stosuje się replikację bazy na drugi węzeł. SQL Server oferuje w tym celu grupy dostępności Always On, opisane w dokumentacji Microsoft Learn. Drugi węzeł może przejąć obciążenie po awarii, a w niektórych konfiguracjach także obsługiwać raporty tylko do odczytu.

Monitorowanie warstw

Każdej warstwie odpowiada inny wskaźnik. Dla serwera aplikacji jest to czas odpowiedzi punktu końcowego, dla integracji liczba wiadomości oczekujących w kolejce, a dla bazy opóźnienie zapisu do dziennika. Ostatni wskaźnik można odczytać z dynamicznego widoku plików bazy.

SELECT DB_NAME(vfs.database_id) AS Baza,
       mf.physical_name         AS Plik,
       vfs.io_stall_write_ms / NULLIF(vfs.num_of_writes, 0) AS SrednieOpoznienieZapisuMs
FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS vfs
JOIN sys.master_files AS mf
  ON mf.database_id = vfs.database_id
 AND mf.file_id     = vfs.file_id
WHERE mf.type_desc = N'LOG';

Reguła: kopia zapasowa, której odtworzenie nie zostało przetestowane, nie jest kopią zapasową.

Uprawnienia i szyfrowanie

Aplikacja łączy się z bazą kontem o minimalnych uprawnieniach: wykonywanie procedur, bez prawa do zmiany schematu. Operator dostaje rolę w aplikacji, a nie w bazie, więc odebranie dostępu nie wymaga zmian w SQL Server. Ruch między terminalem a serwerem chroni protokół TLS, a hasła użytkowników przechowuje się jako skróty z solą, nigdy jawnie. Kryteria doboru całego rozwiązania, łącznie z kosztami utrzymania, omawia strona o wyborze programu do zarządzania magazynem.