W skrócie

Stan ujemny rzadko bierze się z błędnej ilości. W opisywanym wdrożeniu przyczyną było zapytanie, które przy powtórzonym numerze palety czytało datę produkcji z najstarszego przyjęcia. Poprawka zmieniła odczyt na stan palety i objęła sześć zapytań wózka.

Opisujemy przypadek z magazynu operatora logistycznego z południa Polski, który pracuje na platformie Studio WMS.net, a szerszy kontekst takiego magazynu przedstawia strona WMS dla operatora 3PL. Wartości w przykładach są zmyślone, a dat i numerów palet z rzeczywistego zdarzenia nie podajemy. Tekst jest napisany dla działu IT: pokazuje, jak ustalić przyczynę na danych i jak bezpiecznie podmienić zapytania w repozytorium zapytań.

Objaw - minus 9,9 na lokalizacji

Przykład liczbowy wygląda tak. Paleta z towarem w ilości 9,9 zostaje przyjęta dokumentem przyjęcia zewnętrznego (PZ) i wydana dokumentem wydania zewnętrznego (WZ), a oba dokumenty opisuje strona o dokumentach magazynowych. Po kilkudziesięciu dniach ta sama paleta wraca do magazynu pod tym samym numerem, z inną datą produkcji towaru. Dokument zmiany lokalizacji (ZL) przesuwa ją na inną lokalizację.

DokumentIlośćData produkcjiUwaga
PZ+9,9APierwsze przyjęcie palety
WZ-9,9AWydanie palety z magazynu
PZ ponowne+9,9BTa sama paleta, ten sam numer, inna data produkcji
ZLprzesunięcie 9,9A zamiast BZapis daty ze starego przyjęcia

Skutek widać na obu lokalizacjach. Na lokalizacji źródłowej grupa ze starą datą spada do -9,9, a grupa z nową datą zostaje na +9,9. Suma obu grup wynosi zero, więc minus widać dopiero po rozbiciu stanu według daty produkcji. Na lokalizacji docelowej paleta dostaje złą datę, a bieżący stan na lokalizacjach pokazuje strona o bieżących stanach magazynowych.

Numer palety pochodzi z etykiety logistycznej, której budowę opisuje artykuł o trzech kodach etykiety GS1-128. Sposób, w jaki WMS prowadzi stan według partii i dat, wyjaśnia strona o partiach produkcyjnych i datach ważności.

Oś zdarzeń z dokładnością do sekundy

O przyczynie przesądziły czasy zapisane w dokumentach i zadaniach wózka. Dopełnia je historia operacji, w której zdarzenia mają znaczniki czasu z dokładnością do sekundy. ZL założony na ekranie zapisał datę poprawną, zgodną ze stanem. Po około 47 sekundach wózek rozpoczął zadanie, a po kolejnych 4 sekundach zapis realizacji nadpisał datę i partię.

CzasZdarzenieData produkcji w ZL
0 sZL założony na ekraniePoprawna, odczytana ze stanu palety
około 47 sWózek rozpoczyna zadanieBez zmiany
około 51 sZapis realizacjiNadpisana datą z najstarszego przyjęcia

Takie czasy da się odtworzyć dla każdej palety, bo dokumenty i zadania mają znaczniki czasu, tak samo jak historia operacji. Zestawienie wskazuje moment zmiany. Data zmieniła się w zapisie realizacji, a nie przy zakładaniu dokumentu, więc szukamy zapytania, które ten zapis wykonuje.

Przyczyna - TOP 1 bez ORDER BY

Zapytanie wózka czytało partię i datę produkcji poleceniem, które zwraca pierwszy wiersz z danym numerem palety, bez klauzuli ORDER BY. W praktyce wynikiem był najstarszy zapis, czyli dane z pierwszego przyjęcia, którego towar dawno wyszedł z magazynu.

Taki wzorzec działa poprawnie, dopóki numer palety występuje w stanie raz. Problem pojawia się, gdy ta sama paleta ma dwa przyjęcia z różnymi datami, bo zapytanie nie rozstrzyga, które z nich czytać.

TOP 1 bez ORDER BY nie ma gwarantowanej kolejności. Wynik zwykle najstarszy to przypadek, nie reguła.

Dokumentacja Microsoft opisuje, że klauzula TOP użyta bez klauzuli ORDER BY zwraca wiersze w nieokreślonej kolejności. Wynik, który zwykle jest najstarszym zapisem, zależy więc od planu zapytania, a ten może się zmienić. Zależy też od tego, którym indeksem silnik przechodzi po tabeli, więc po przebudowie indeksu albo zmianie statystyk to samo zapytanie mogłoby zwrócić inny wiersz.

Ten sam wzorzec miało sześć zapytań wózka. Zestawienie podaje pięć czynności, w których występował.

Czynność wózkaRola zapytania ze wzorcem
Zapis ZLOdczyt partii i daty przy zapisie realizacji zmiany lokalizacji
Zamiana paletOdczyt partii i daty przy zamianie palety proponowanej na zeskanowaną
Paleta innego indeksuOdczyt partii i daty przy skanie palety o innym indeksie
Nowa pozycja ZL do strefy pośredniejOdczyt partii i daty przy dopisaniu pozycji ZL
Kontrola zgodności paletyOdczyt partii i daty przy sprawdzeniu, czy paleta pasuje do pozycji

Poprawka - odczyt ze stanu palety

Poprawka podmienia jeden wzorzec. Zamiast pierwszego wiersza z danym numerem palety zapytania wywołują funkcję tabelaryczną, która czyta ze stanu palety partię oraz dwie daty: produkcji i terminu. Funkcja wybiera grupę w ustalonej kolejności.

  1. Grupa z dodatnią ilością.
  2. Preferencja lokalizacji źródłowej.
  3. Preferencja partii.
  4. Najnowszy ruch.

Pierwsza reguła odrzuca grupy puste i ujemne, bo tylko grupa z towarem opisuje paletę, która faktycznie stoi w magazynie. Z wyboru wyłączona jest własna para ZL i własny dokument. Gdy palety nie ma na stanie, funkcja bierze dane z ostatniego przyjęcia. Funkcja zajmuje miejsce wzorca w sześciu zapytaniach, a pozostałe elementy zapytań zostają takie, jakie były.

W zapytaniach typu UPDATE przy braku wyniku zostaje wartość już zapisana w ZL. Poza podmienionym wzorcem nic się nie zmieniło: skrypty przeglądarki i parametry zapytań zostały takie, jakie były.

Jak znaleźć inne palety z tym samym problemem

Zapytanie uruchamiane cyklicznie w zadaniu agenta SQL pilnuje stanów ujemnych. Grupuje ono stany według siedmiu pól (indeks, lokalizacja, magazyn, paleta, seria, kod palety i data produkcji) i zostawia grupy, w których suma ilości jest mniejsza od zera. Ponieważ grupowanie obejmuje datę produkcji, minus ujawnia się nawet wtedy, gdy suma na lokalizacji wynosi zero.

Drugie zapytanie wskazuje palety z więcej niż jedną datą produkcji, czyli kandydatów na ten sam problem, zanim powstanie minus. Zapytanie monitorujące działa według harmonogramu, więc minus trafia na listę przy najbliższym przebiegu, a nie dopiero przy inwentaryzacji. Wynik pierwszego zapytania da się czytać jak listę kontrolną, bo każda grupa z sumą poniżej zera wskazuje paletę i lokalizację do sprawdzenia w dokumentach. Identyfikowalność partii i palet w dokumentach opisuje strona o Track and Trace w magazynie.

Wdrożenie zmiany w repozytorium zapytań

Zapytania wózka nie siedzą w kodzie przeglądarki. Przeglądarka przekazuje identyfikator, a treść zapytania leży w repozytorium zapytań w bazie. Poprawka jest więc zmianą danych w bazie klienta i dostaje własny skrypt wdrożeniowy.

  • Zgodność treści. Treść w bazie musi zgadzać się z kopią w pliku skryptu.
  • Liczba wystąpień. Skrypt liczy wystąpienia zmienianego fragmentu przed zapisem.
  • Kompilacja. Nowa treść jest kompilowana przed zapisem do bazy.
  • Tryb próbny. Skrypt można uruchomić bez zapisu zmian.

Do skryptu dołączony jest osobny skrypt wycofania z oryginalnymi treściami zapytań. Po zapisie skrypt resetuje pamięć podręczną repozytorium zapytań, żeby wózki zaczęły używać nowej treści. Kolejność wydań z magazynu, w której data przyjęcia palety odgrywa własną rolę, opisuje artykuł o FIFO według daty przyjęcia palety.

Pytania i odpowiedzi o stanie ujemnym w WMS

Czym jest stan ujemny w WMS?

Stan ujemny oznacza, że suma ilości w grupie stanu jest mniejsza od zera, na przykład dla jednej palety i daty produkcji na jednej lokalizacji. W opisanym przypadku minus pojawił się w grupie ze starą datą, bo przesunięcie zapisało datę z innego przyjęcia.

Dlaczego TOP 1 bez ORDER BY jest ryzykowne?

Bez klauzuli ORDER BY SQL Server nie gwarantuje kolejności wierszy, więc pierwszy wiersz bywa przypadkowy. W opisanym wdrożeniu wynikiem był zwykle najstarszy zapis, ale to przypadek, a nie reguła.

Jak sprawdzić, które palety mają więcej niż jedną datę produkcji?

Służy do tego drugie zapytanie monitorujące, które wskazuje palety z więcej niż jedną datą produkcji. Pierwsze zapytanie grupuje stany i pokazuje grupy z sumą poniżej zera.

Jak bezpiecznie wdrożyć zmianę zapytań w bazie klienta?

Skrypt sprawdza zgodność treści w bazie z kopią w pliku oraz liczbę wystąpień zmienianego fragmentu. Nową treść kompiluje przed zapisem, a ma też tryb próbny i osobny skrypt wycofania. Po zapisie resetuje pamięć podręczną repozytorium zapytań.

Czy numer SSCC może się powtórzyć?

Numer identyfikuje fizyczną paletę, więc ta sama paleta wracająca do magazynu może pojawić się w dokumentach pod tym samym numerem. W opisanym wdrożeniu tak się stało, a towar na niej miał inną datę produkcji. System musi więc odróżniać kolejne przyjęcia po stanie palety i dacie, a nie po samym numerze.

Słownik pojęć o stanie ujemnym w WMS

Terminy używane przez dział IT, gdy szuka przyczyny stanu ujemnego i poprawia zapytania w bazie WMS.

Stan ujemny
Suma ilości w grupie stanu, na przykład dla palety i daty produkcji na jednej lokalizacji, mniejsza od zera. Zwykle wskazuje rozjazd między dokumentami a odczytem stanu.
TOP
Klauzula T-SQL ograniczająca liczbę zwracanych wierszy. Bez ORDER BY wskazuje wiersze w nieokreślonej kolejności.
ORDER BY
Klauzula T-SQL, która ustala kolejność wierszy wyniku. Dopiero z nią TOP zwraca przewidywalne wiersze.
Funkcja tabelaryczna
Funkcja bazy danych zwracająca tabelę, którą zapytanie odpytuje jak widok. W opisanej poprawce czyta partię i daty ze stanu palety.
SSCC
Numer jednostki logistycznej, na przykład palety. Generuje go procedura, a drukuje się go na etykiecie. Identyfikuje paletę w dokumentach i na stanie.
Tryb próbny
Uruchomienie skryptu wdrożeniowego bez zapisu zmian w bazie. Pokazuje, co skrypt zmieniłby.
Repozytorium zapytań
Zbiór zapytań zapisany w bazie i wywoływany przez identyfikator. Przeglądarka wózka nie wysyła treści SQL.

Opisz nam problem ze stanami

Podaj, na jakich lokalizacjach i dokumentach widzisz stany ujemne, a przygotujemy plan diagnozy na danych.