Studio WMS.net - instrukcja obsługi

Komunikaty agencji celnej: kontrakt JSON i przetwarzanie w bazie - dla wdrożeniowców

Agencja celna zgłasza towar i odbiera zwolnienie poza magazynem, a moduł Dozór celny dostaje z tego tylko komunikat JSON. Komunikat trafia do kolejki, jest przetwarzany w jednej transakcji i zmienia statusy partii. W wersji demonstracyjnej punkt API przyjmujący komunikaty nie istnieje: komunikat powstaje przez symulację albo przez import pliku w tym samym kontrakcie. Ekran kolejki opisuje strona użytkownika.

Na tej stronie

  1. Kanały odbioru
  2. Kontrakt JSON
  3. Hash treści i duplikaty
  4. Przetwarzanie komunikatu
  5. Dopasowanie pozycji i wynik częściowy
  6. Błędy przetwarzania
  7. Ponowne przetwarzanie zapisanej treści
  8. Transport pliku przez UTF-16LE
  9. Zapytania diagnostyczne
  10. Najczęstsze pytania
  11. Słownik pojęć

Kanały odbioru

Kolumna KANAL w kolejce przyjmuje trzy wartości kodowe (API, FTPS, PLIK). Procedura zapisu komunikatu dla każdej innej wartości ustawia PLIK. W bazie demonstracyjnej działają dwa sposoby wprowadzenia komunikatu: przycisk Symuluj komunikat agencji, który zapisuje komunikat z kanałem API, oraz przycisk Wczytaj komunikat z pliku (JSON), który zapisuje go z kanałem PLIK. Punkt API przyjmujący komunikaty od agencji nie jest częścią wersji demo, a komunikaty z kanałem FTPS w danych demonstracyjnych są tylko wpisami w kolejce.

Przy wczytywaniu pliku ekran przekazuje agencję wybraną w filtrze Agencja celna. Gdy agencji nie wskazano, a komunikat jest poprawnym JSON, agencję przypisuje pole agencjaNip. Gdy NIP nie pasuje do żadnej aktywnej agencji, komunikat trafia do kolejki bez agencji i pozostaje przetwarzalny.

Kontrakt JSON

Komunikat to jeden obiekt JSON. Poniższe przykłady używają danych z bazy demonstracyjnej: numeru dokumentu i kontenera z dokumentu przygotowanego pod demonstrację PZC oraz agencji demonstracyjnej. Wartości klienta i indeksów są przykładowe, a w prawdziwym komunikacie muszą istnieć w kartotece klienta.

Komunikat ZGLOSZENIE:

{
  "typKomunikatu": "ZGLOSZENIE",
  "typDokumentu": "T1",
  "mrn": "26PLDEMO000000901H",
  "klientNip": "1234567890",
  "agencjaNip": "5250000001",
  "kodProcedury": "71",
  "pozycje": [
    { "lp": 1, "indeks": "DEMO-3001", "ilosc": 120, "jm": "szt", "masaBruttoKg": 960.5, "nrKontenera": "DEMU2000015" },
    { "lp": 2, "indeks": "DEMO-3002", "ilosc": 80, "jm": "szt", "masaBruttoKg": 410 }
  ]
}

Komunikat ZWOLNIENIE do tego samego dokumentu różni się rodzajem i obowiązkową datą zwolnienia:

{
  "typKomunikatu": "ZWOLNIENIE",
  "typDokumentu": "T1",
  "mrn": "26PLDEMO000000901H",
  "klientNip": "1234567890",
  "agencjaNip": "5250000001",
  "kodProcedury": "71",
  "dataZwolnienia": "2026-10-17T09:42:00+02:00",
  "pozycje": [
    { "lp": 1, "indeks": "DEMO-3001", "ilosc": 120, "jm": "szt" },
    { "lp": 2, "indeks": "DEMO-3002", "ilosc": 80, "jm": "szt" }
  ]
}
PoleTypWymaganeZnaczenie
typKomunikatuteksttakZGLOSZENIE albo ZWOLNIENIE; wielkość liter bez znaczenia.
typDokumentuteksttakT1, DSK albo MRN.
mrnteksttakNumer dokumentu celnego; nazwa pola jest ta sama także dla T1 i DSK. Spacje na brzegach są obcinane, litery zamieniane na wielkie.
klientNipteksttak, gdy brak klientIdNIP klienta; myślniki i spacje są pomijane przy porównaniu z kartoteką kontrahentów.
klientIdliczba lub teksttak, gdy brak klientNipIdentyfikator kontrahenta; używany wyłącznie przy pustym klientNip, w demo.
agencjaNiptekstnieWskazuje agencję z kartoteki przy zapisie komunikatu do kolejki.
kodProcedurytekst, 2 znakinieKod procedury; zapisywany przy tworzeniu dokumentu i aktualizowany przy ZGLOSZENIE, gdy podany.
dataZwolnieniatekst ISO 8601tak dla ZWOLNIENIECzytane jest pierwszych 10 znaków jako data; reszta (godzina, strefa) jest pomijana.
pozycjetablicazalecanePozycje dokumentu. Bez pozycji zmienia się status dokumentu, ale statusy partii nie.
pozycje[].lpliczbanieNumer pozycji; widoczny w komunikacie błędu.
pozycje[].indeksteksttakIndeks z kartoteki asortymentu klienta.
pozycje[].iloscliczbatakIlość zgłoszona; trafia do pozycji nowo tworzonego dokumentu.
pozycje[].jmtekstnieJednostka miary; przy braku brana z kartoteki indeksu.
pozycje[].masaBruttoKgliczbanieMasa brutto w kg.
pozycje[].nrKonteneratekstnieZawęża dopasowanie partii do kontenera.

Komunikat symulowany z ekranu ma zawsze dwa pola klienta (klientNip oraz klientId), a przy przetwarzaniu liczy się NIP, jeśli jest niepusty. Data zwolnienia w symulacji to bieżąca data i godzina z zapisaną na sztywno strefą +02:00.

Hash treści i duplikaty

Przy zapisie do kolejki procedura liczy SHA-256 z odczytanej treści komunikatu i zapisuje wynik w kolumnie HASH_TRESCI z unikalnym indeksem UQ_x_cel_import_HASH. Jeśli komunikat o identycznej treści już jest w kolejce, nowy wiersz nie powstaje, a procedura zwraca OK z komunikatem: Ten komunikat był już odebrany (data) - duplikat rozpoznany po hashu treści, stan bez zmian. Zwracany jest identyfikator i status istniejącego wiersza, więc ponowne wczytanie tego samego pliku nie przetwarza komunikatu drugi raz.

Hash obejmuje całą treść znak po znaku, także białe znaki. Ten sam komunikat zapisany z innym wcięciem daje inny hash i zostanie potraktowany jak nowy. Przed ponownym wgraniem skorygowanego komunikatu nie trzeba więc kasować starego wiersza.

SELECT ID, KANAL, NUMER, STATUS_PRZETW,
       LOWER(CONVERT(varchar(64), HASH_TRESCI, 2)) AS HASH_SHA256
FROM dbo.x_cel_import
ORDER BY ID DESC;

Przetwarzanie komunikatu

Przetwarzanie wykonuje jedna procedura, wywoływana zaraz po zapisie do kolejki oraz przyciskiem Ponów wybrany komunikat. Procedura działa z XACT_ABORT i limitem oczekiwania na blokadę 5000 ms.

Przetwarzanie komunikatu od agencji celnejKomunikat trafia do kolejki. Powtórzony komunikat jest rozpoznawany po haszu treści i nie zmienia stanu. Poprawny komunikat jest przetwarzany w jednej transakcji z wynikiem Przetworzony, Częściowy albo Błąd, a błąd cofa wszystkie zmiany.Komunikat agencjikanał API, FTPS albo plikJSONHash SHA-256 treściodcisk tekstu komunikatuTaki hashjuż jest?TAK: duplikatstan bez zmianNIEKontrola komunikatustruktura JSON, typ komunikatu idokumentu, numer, klient po NIP, indeksyz kartoteki klientabłędy 50101-50107Jedna transakcjadokument celny: nowy albo zmiana statusupartie dokumentu: status 2 albo 3, wpishistorii IMPORTdopasowanie po indeksie i kontenerzePrzetworzonyzmieniono status partiiCzęściowypozycja bez partii albo partiazablokowanaBłądcofnięcie wszystkichzmianPonów po uzupełnieniukartoteki
Przetwarzanie komunikatu: kontrola hasha, walidacja, transakcja zmieniająca statusy partii oraz trzy możliwe wyniki zapisane w kolejce.

Kolejność kroków jest stała.

  • Walidacja struktury (50101-50105) - treść musi być poprawnym JSON, a pola rodzaju komunikatu oraz typu i numeru dokumentu muszą mieć dopuszczalne wartości; ZWOLNIENIE wymaga dataZwolnienia.
  • Klient (50106) - kontrahent jest szukany w kartotece po NIP bez myślników i spacji, a przy pustym NIP po identyfikatorze klientId.
  • Mapowanie indeksów (50107) - każdy indeks z pozycji musi istnieć w kartotece asortymentu tego klienta; pierwsza pozycja bez dopasowania przerywa przetwarzanie.
  • Transakcja - dopiero po trzech powyższych krokach procedura otwiera transakcję, czyta dokument z blokadą aktualizacji i zmienia dane.

W transakcji procedura szuka dokumentu celnego po typie i numerze oraz po kliencie. Dla istniejącego dokumentu ZGLOSZENIE podnosi status dokumentu do 2, jeśli był niższy, a ZWOLNIENIE ustawia status 3 i datę zwolnienia. Dla nieznanego dokumentu zachowanie zależy od rodzaju komunikatu: ZGLOSZENIE tworzy nowy dokument, a ZWOLNIENIE kończy się błędem 50108. Nowy dokument dostaje status 2, dzisiejszą datę przyjęcia, magazyn z wiersza kolejki, uwagę Utworzony z importu komunikatu agencji. oraz pozycje z komunikatu. Partii nie tworzy, bo te powstają dopiero na PZC.

Następnie procedura zmienia statusy partii dokumentu. Komunikat ZGLOSZENIE przestawia na 2 partie ze statusem 1, a ZWOLNIENIE przestawia na 3 partie ze statusem 1 albo 2. Partia podlega zmianie, gdy jej indeks występuje w pozycjach komunikatu i, jeśli pozycja podaje nrKontenera, gdy kontener się zgadza. Dla każdej zmienionej partii powstaje wpis historii ze źródłem IMPORT, uzasadnieniem w postaci Zgłoszenie lub Zwolnienie towaru wraz z typem i numerem dokumentu oraz identyfikatorem komunikatu. Partie zablokowane (status 5) nie są zmieniane.

Dopasowanie pozycji i wynik częściowy

Dopasowanie pozycji do partii odbywa się po indeksie, a opcjonalnie po kontenerze. Po zmianie statusów procedura sprawdza, czy każda pozycja komunikatu ma partię do dopasowania na dokumencie. Sprawdzenie dotyczy tylko dokumentów, które mają już jakieś partie. Pozycja bez takiej partii, a także pozycja, której jedyną partią jest partia zablokowana, trafia do uwag, a komunikat dostaje wynik Częściowy. Treść uwag zapisuje się w kolumnie BLAD i na karcie komunikatu pod nazwą Uwagi przetwarzania.

Wynik przetwarzania trafia do kolumny STATUS_PRZETW jako 1 (Przetworzony), 3 (Częściowy) albo 2 (Błąd). Status 0 (Oczekuje) mają komunikaty zapisane, ale jeszcze nieprzetworzone. Przy wyniku Częściowym zmiany statusów partii, które się powiodły, zostają zapisane. Przy Błędzie transakcja jest cofana w całości.

Błędy przetwarzania

KodTreść komunikatuPrzyczyna
50101Błąd struktury: treść komunikatu nie jest poprawnym JSON.Treść nie przechodzi ISJSON.
50102Błąd struktury: typKomunikatu musi być ZGLOSZENIE albo ZWOLNIENIE.Brak pola albo inna wartość.
50103Błąd struktury: typDokumentu musi być T1, DSK albo MRN.Brak pola albo inna wartość.
50104Błąd struktury: brak numeru dokumentu (mrn).Puste pole mrn.
50105Błąd struktury: komunikat ZWOLNIENIE wymaga dataZwolnienia.Brak daty albo data w nierozpoznanym formacie.
50106Nieznany klient (NIP lub identyfikator klienta nie występuje w kartotece kontrahentów).Żaden aktywny kontrahent roli WMS nie pasuje.
50107Pozycja N: indeks X nie istnieje w kartotece klienta (skrót klienta). Transakcja została cofnięta, statusy partii nie zmieniły się.Indeks spoza kartoteki asortymentu klienta.
50108Nieznany dokument celny - zwolnienie nie ma czego zwolnić (najpierw zgłoszenie albo dokument w rejestrze).ZWOLNIENIE dla dokumentu, którego nie ma w rejestrze klienta.
1222Partie są właśnie zatwierdzane w wydaniu (blokada wiersza) - ponów komunikat za chwilę.Przekroczony limit oczekiwania na blokadę wiersza partii.

Kod 1222 to systemowy błąd SQL Server, nie błąd aplikacyjny: procedura zamienia go na czytelny tekst i zapisuje komunikat ze statusem Błąd. Wynika z tego, że wiersze partii czytane w transakcji importu i w zatwierdzeniu WZC blokują się wzajemnie, a oczekiwanie trwa najwyżej 5 sekund. Po takim błędzie wystarczy ponowić komunikat. Szczegóły blokad opisuje strona kontrola wydania i transakcje.

Ponowne przetwarzanie zapisanej treści

Przycisk Ponów wybrany komunikat wywołuje tę samą procedurę przetwarzania, ale z zapisaną już treścią, więc nie tworzy nowego wiersza w kolejce i nie liczy hasha od nowa. Ekran nie pozwala ponowić komunikatu o statusie Przetworzony. Po błędzie 50107 typowa naprawa to dopisanie brakującego indeksu w kartotece klienta i ponowienie tego samego komunikatu, bez zmiany treści. Po błędzie 50108 wystarczy najpierw przetworzyć komunikat ZGLOSZENIE albo założyć dokument w rejestrze.

Transport pliku przez UTF-16LE

Treść komunikatu wczytywanego z pliku przeglądarka przesyła do bazy jako ciąg szesnastkowy kodowania UTF-16LE, bez przedrostka 0x. Dzięki temu treść nie koliduje z separatorem parametrów w zapytaniu ani z apostrofem. Procedura odczytuje ją wywołaniem CONVERT do varbinary z formatem 2, a potem CAST do nvarchar. Każdy znak zajmuje dwa bajty z młodszym bajtem na początku, na przykład nawias klamrowy otwierający to 7B00, a litera A to 4100.

Jeżeli konwersja się nie powiedzie, procedura zwraca komunikat Nie udało się odczytać pliku komunikatu. Pusty plik daje komunikat Plik komunikatu jest pusty. Plik może mieć najwyżej 200 kB, powinien być zapisany w UTF-8 i mieć rozszerzenie .json albo .txt. Hash liczony jest z już odczytanego tekstu, nie z bajtów pliku, więc kodowanie pliku nie zmienia hasha tego samego tekstu.

Zapytania diagnostyczne

Pierwsze zapytanie pokazuje stan kolejki według statusów, a drugie wymienia komunikaty z błędem lub wynikiem częściowym razem z treścią błędu. Oba tylko czytają dane.

SELECT i.STATUS_PRZETW,
       CASE i.STATUS_PRZETW WHEN 0 THEN 'Oczekuje' WHEN 1 THEN 'Przetworzony'
                            WHEN 2 THEN 'Błąd' WHEN 3 THEN 'Częściowy' END AS STATUS,
       COUNT(*) AS LICZBA, MIN(i.DATA_ODBIORU) AS NAJSTARSZY
FROM dbo.x_cel_import i
GROUP BY i.STATUS_PRZETW
ORDER BY i.STATUS_PRZETW;
SELECT i.ID, i.DATA_ODBIORU, i.KANAL, i.TYP_KOMUNIKATU, i.TYP_DOKUMENTU, i.NUMER,
       i.STATUS_PRZETW, i.BLAD, ISJSON(i.TRESC) AS CZY_JSON
FROM dbo.x_cel_import i
WHERE i.STATUS_PRZETW IN (2, 3)
ORDER BY i.DATA_ODBIORU DESC;

Najczęstsze pytania

Czy w wersji demo agencja może wysłać komunikat przez API?

Nie. Punkt API przyjmujący komunikaty nie jest częścią wersji demo. Komunikat tworzy się przyciskiem Symuluj komunikat agencji albo wczytuje z pliku JSON w tym samym kontrakcie.

Co się stanie przy wczytaniu tego samego pliku drugi raz?

Baza rozpozna duplikat po hashu SHA-256 treści, nie doda wiersza do kolejki i zwróci komunikat o duplikacie. Stan partii pozostanie bez zmian.

Dlaczego komunikat ZWOLNIENIE kończy się błędem 50108?

Dokumentu o podanym typie, numerze i kliencie nie ma w rejestrze. Najpierw trzeba przetworzyć komunikat ZGLOSZENIE, który tworzy dokument, albo dodać dokument w rejestrze.

Czy błąd w jednej pozycji cofa cały komunikat?

Tak, błąd 50107 i inne błędy rzucone w transakcji cofają wszystkie zmiany tego komunikatu. Komunikat dostaje status Błąd, a statusy partii się nie zmieniają.

Jaki jest limit rozmiaru pliku komunikatu?

Ekran przyjmuje pliki .json i .txt do 200 kB w kodowaniu UTF-8.

Słownik pojęć

ZGLOSZENIE
Rodzaj komunikatu agencji: zgłoszenie dokumentu z numerem; przestawia partie ze statusu Pod dozorem na Zgłoszony.
ZWOLNIENIE
Rodzaj komunikatu agencji: zwolnienie towaru z datą; przestawia partie ze statusu Pod dozorem albo Zgłoszony na Zwolniony.
Kolejka importu
Tabela komunikatów od agencji ze statusem przetwarzania: Oczekuje, Przetworzony, Błąd albo Częściowy.
Hash treści
Skrót SHA-256 treści komunikatu z unikalnym indeksem; rozpoznaje duplikaty.
Wynik częściowy
Komunikat przetworzony, w którym część pozycji nie miała partii do dopasowania albo trafiła na partię zablokowaną.
UTF-16LE szesnastkowo
Sposób przesłania treści pliku do bazy: każdy znak jako dwa bajty zapisane cyframi szesnastkowymi.